368 lines
12 KiB
TypeScript
368 lines
12 KiB
TypeScript
/**
|
||
* 获取波形预览组件的样式内容(纯 CSS,不包含 style 标签)
|
||
*/
|
||
export function getWaveformPreviewContent(): string {
|
||
return `
|
||
/* 波形预览组件样式 */
|
||
.waveform-preview {
|
||
margin: 16px 0;
|
||
border: 1px solid var(--vscode-panel-border);
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
background: var(--vscode-editor-background);
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
transition: box-shadow 0.3s ease, transform 0.2s ease;
|
||
}
|
||
.waveform-preview:hover {
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||
transform: translateY(-2px);
|
||
}
|
||
.waveform-preview-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 14px 16px;
|
||
background: linear-gradient(135deg, var(--vscode-input-background) 0%, var(--vscode-editor-background) 100%);
|
||
border-bottom: 1px solid var(--vscode-panel-border);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
.waveform-preview-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--vscode-foreground);
|
||
letter-spacing: 0.3px;
|
||
}
|
||
.waveform-preview-title svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
color: var(--vscode-button-background);
|
||
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
|
||
}
|
||
.waveform-expand-btn {
|
||
padding: 6px 14px;
|
||
background: var(--vscode-button-background);
|
||
color: var(--vscode-button-foreground);
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
transition: all 0.2s ease;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
.waveform-expand-btn:hover {
|
||
background: var(--vscode-button-hoverBackground);
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||
}
|
||
.waveform-expand-btn:active {
|
||
transform: translateY(0);
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||
}
|
||
.waveform-expand-btn svg {
|
||
width: 14px;
|
||
height: 14px;
|
||
}
|
||
.waveform-preview-content {
|
||
padding: 12px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
background: var(--vscode-editor-background);
|
||
}
|
||
.waveform-preview-canvas {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
.waveform-preview-placeholder {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--vscode-descriptionForeground);
|
||
font-size: 13px;
|
||
text-align: center;
|
||
padding: 20px;
|
||
}
|
||
.waveform-preview-placeholder svg {
|
||
width: 48px;
|
||
height: 48px;
|
||
margin-bottom: 12px;
|
||
opacity: 0.5;
|
||
}
|
||
.waveform-info {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: var(--vscode-descriptionForeground);
|
||
}
|
||
.waveform-mini-viewer {
|
||
width: 100%;
|
||
height: auto;
|
||
min-height: 120px;
|
||
background: var(--vscode-editor-background);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.waveform-loading {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
color: var(--vscode-descriptionForeground);
|
||
font-size: 12px;
|
||
}
|
||
`;
|
||
}
|
||
|
||
/**
|
||
* 获取波形预览组件的 JavaScript 代码
|
||
*/
|
||
export function getWaveformPreviewScript(): string {
|
||
return `
|
||
/**
|
||
* 创建波形预览组件
|
||
*/
|
||
function createWaveformPreview(vcdFilePath, fileName) {
|
||
const previewDiv = document.createElement('div');
|
||
previewDiv.className = 'waveform-preview';
|
||
|
||
// 头部
|
||
const header = document.createElement('div');
|
||
header.className = 'waveform-preview-header';
|
||
|
||
const title = document.createElement('div');
|
||
title.className = 'waveform-preview-title';
|
||
title.innerHTML = \`
|
||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M128 512h128l64-128 64 128 64-256 64 384 64-128h320"
|
||
stroke="currentColor"
|
||
stroke-width="64"
|
||
fill="none"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"/>
|
||
</svg>
|
||
<span>波形预览 - \${fileName}</span>
|
||
\`;
|
||
|
||
const expandBtn = document.createElement('button');
|
||
expandBtn.className = 'waveform-expand-btn';
|
||
expandBtn.innerHTML = \`
|
||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M342 88.3h340c56.6 0 102.6 46 102.6 102.6v340c0 56.6-46 102.6-102.6 102.6H342c-56.6 0-102.6-46-102.6-102.6v-340c0-56.6 46-102.6 102.6-102.6z"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="48"/>
|
||
<path d="M239.4 390.5v340c0 56.6 46 102.6 102.6 102.6h340"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="48"
|
||
stroke-linecap="round"/>
|
||
</svg>
|
||
展开查看
|
||
\`;
|
||
expandBtn.onclick = () => openFullWaveform(vcdFilePath);
|
||
|
||
header.appendChild(title);
|
||
header.appendChild(expandBtn);
|
||
|
||
// 内容区域 - 创建一个唯一ID的容器用于显示波形
|
||
const content = document.createElement('div');
|
||
content.className = 'waveform-preview-content';
|
||
|
||
const miniViewerId = 'waveform-mini-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
|
||
const miniViewer = document.createElement('div');
|
||
miniViewer.id = miniViewerId;
|
||
miniViewer.className = 'waveform-mini-viewer';
|
||
|
||
// 添加加载提示
|
||
const loadingDiv = document.createElement('div');
|
||
loadingDiv.className = 'waveform-loading';
|
||
loadingDiv.textContent = '正在加载波形预览...';
|
||
miniViewer.appendChild(loadingDiv);
|
||
|
||
content.appendChild(miniViewer);
|
||
|
||
previewDiv.appendChild(header);
|
||
previewDiv.appendChild(content);
|
||
|
||
// 异步加载波形数据
|
||
loadMiniWaveform(miniViewerId, vcdFilePath, loadingDiv);
|
||
|
||
return previewDiv;
|
||
}
|
||
|
||
/**
|
||
* 加载迷你波形预览
|
||
*/
|
||
async function loadMiniWaveform(containerId, vcdFilePath, loadingDiv) {
|
||
try {
|
||
// 请求 VCD 文件信息
|
||
vscode.postMessage({
|
||
command: 'getVCDInfo',
|
||
vcdFilePath: vcdFilePath,
|
||
containerId: containerId
|
||
});
|
||
} catch (error) {
|
||
console.error('加载波形预览失败:', error);
|
||
loadingDiv.textContent = '波形预览加载失败';
|
||
loadingDiv.style.color = 'var(--vscode-errorForeground)';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 渲染波形预览信息
|
||
*/
|
||
function renderWaveformInfo(containerId, vcdInfo) {
|
||
const container = document.getElementById(containerId);
|
||
if (!container) return;
|
||
|
||
// 清空容器
|
||
container.innerHTML = '';
|
||
|
||
// 绘制真实波形
|
||
const waveformSvg = document.createElement('div');
|
||
waveformSvg.innerHTML = drawRealWaveform(vcdInfo.signals || []);
|
||
|
||
container.appendChild(waveformSvg);
|
||
}
|
||
|
||
/**
|
||
* 绘制真实波形
|
||
*/
|
||
function drawRealWaveform(signals) {
|
||
if (!signals || signals.length === 0) {
|
||
return \`
|
||
<svg width="100%" height="80" viewBox="0 0 800 80" style="background: var(--vscode-editor-background);">
|
||
<text x="400" y="40" fill="var(--vscode-descriptionForeground)" font-size="12" text-anchor="middle">
|
||
无波形数据
|
||
</text>
|
||
</svg>
|
||
\`;
|
||
}
|
||
|
||
const svgWidth = 800;
|
||
const svgHeight = Math.max(80, signals.length * 30 + 20);
|
||
const signalHeight = 20;
|
||
const signalSpacing = 30;
|
||
const leftMargin = 80;
|
||
const rightMargin = 20;
|
||
const waveformWidth = svgWidth - leftMargin - rightMargin;
|
||
|
||
const colors = ['var(--vscode-charts-blue)', 'var(--vscode-charts-green)', 'var(--vscode-charts-orange)'];
|
||
|
||
let svgContent = \`<svg width="100%" height="\${svgHeight}" viewBox="0 0 \${svgWidth} \${svgHeight}" style="background: var(--vscode-editor-background);">\`;
|
||
|
||
// 绘制每个信号
|
||
signals.forEach((signal, index) => {
|
||
const y = 10 + index * signalSpacing;
|
||
const color = colors[index % colors.length];
|
||
|
||
// 绘制信号名称
|
||
svgContent += \`<text x="5" y="\${y + signalHeight / 2 + 4}" fill="var(--vscode-foreground)" font-size="10" opacity="0.8">\${signal.name}</text>\`;
|
||
|
||
// 如果没有值变化数据,显示提示
|
||
if (!signal.values || signal.values.length === 0) {
|
||
svgContent += \`<text x="\${leftMargin + waveformWidth / 2}" y="\${y + signalHeight / 2 + 4}" fill="var(--vscode-descriptionForeground)" font-size="9" text-anchor="middle" opacity="0.5">无数据</text>\`;
|
||
return;
|
||
}
|
||
|
||
// 计算时间范围
|
||
const times = signal.values.map(v => v.time);
|
||
const minTime = Math.min(...times);
|
||
const maxTime = Math.max(...times);
|
||
const timeRange = maxTime - minTime || 1;
|
||
|
||
// 绘制波形
|
||
if (signal.width === 1) {
|
||
// 单比特信号 - 绘制数字波形
|
||
let pathData = '';
|
||
const yHigh = y;
|
||
const yLow = y + signalHeight;
|
||
|
||
signal.values.forEach((point, i) => {
|
||
const x = leftMargin + ((point.time - minTime) / timeRange) * waveformWidth;
|
||
const currentY = (point.value === '1') ? yHigh : yLow;
|
||
|
||
if (i === 0) {
|
||
pathData = \`M \${x} \${currentY}\`;
|
||
} else {
|
||
const prevValue = signal.values[i - 1].value;
|
||
const prevY = (prevValue === '1') ? yHigh : yLow;
|
||
if (prevY !== currentY) {
|
||
pathData += \` L \${x} \${prevY} L \${x} \${currentY}\`;
|
||
} else {
|
||
pathData += \` L \${x} \${currentY}\`;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 延伸到右边界
|
||
const lastValue = signal.values[signal.values.length - 1].value;
|
||
const lastY = (lastValue === '1') ? yHigh : yLow;
|
||
pathData += \` L \${leftMargin + waveformWidth} \${lastY}\`;
|
||
|
||
svgContent += \`<path d="\${pathData}" stroke="\${color}" stroke-width="1.5" fill="none"/>\`;
|
||
} else {
|
||
// 多比特信号 - 绘制总线波形(上下双线)
|
||
const yTop = y + 5;
|
||
const yBottom = y + signalHeight - 5;
|
||
const transitionWidth = 4;
|
||
|
||
let topPath = \`M \${leftMargin} \${yTop}\`;
|
||
let bottomPath = \`M \${leftMargin} \${yBottom}\`;
|
||
|
||
signal.values.forEach((point, i) => {
|
||
const x = leftMargin + ((point.time - minTime) / timeRange) * waveformWidth;
|
||
|
||
// 上线和下线都延伸到变化点
|
||
topPath += \` L \${x} \${yTop}\`;
|
||
bottomPath += \` L \${x} \${yBottom}\`;
|
||
|
||
// 绘制梯形过渡
|
||
topPath += \` L \${x + transitionWidth} \${yBottom} L \${x + transitionWidth} \${yTop}\`;
|
||
bottomPath += \` L \${x + transitionWidth} \${yTop} L \${x + transitionWidth} \${yBottom}\`;
|
||
});
|
||
|
||
// 延伸到右边界
|
||
topPath += \` L \${leftMargin + waveformWidth} \${yTop}\`;
|
||
bottomPath += \` L \${leftMargin + waveformWidth} \${yBottom}\`;
|
||
|
||
svgContent += \`<path d="\${topPath}" stroke="\${color}" stroke-width="1.5" fill="none"/>\`;
|
||
svgContent += \`<path d="\${bottomPath}" stroke="\${color}" stroke-width="1.5" fill="none"/>\`;
|
||
}
|
||
});
|
||
|
||
// 绘制时间轴
|
||
const timeAxisY = svgHeight - 5;
|
||
svgContent += \`<line x1="\${leftMargin}" y1="\${timeAxisY}" x2="\${leftMargin + waveformWidth}" y2="\${timeAxisY}" stroke="var(--vscode-foreground)" stroke-width="1" opacity="0.2"/>\`;
|
||
|
||
svgContent += \`</svg>\`;
|
||
|
||
return svgContent;
|
||
}
|
||
|
||
/**
|
||
* 打开完整波形查看器(在新列中)
|
||
*/
|
||
function openFullWaveform(vcdFilePath) {
|
||
vscode.postMessage({
|
||
command: 'openWaveformViewer',
|
||
vcdFilePath: vcdFilePath
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 在消息中添加波形预览
|
||
*/
|
||
function addWaveformPreviewToMessage(messageDiv, vcdFilePath, fileName) {
|
||
const preview = createWaveformPreview(vcdFilePath, fileName);
|
||
messageDiv.appendChild(preview);
|
||
}
|
||
`;
|
||
}
|