Update Terminal.vue
This commit is contained in:
@@ -34,6 +34,9 @@ let resizeObserver: ResizeObserver | null = null;
|
|||||||
let observedElement: HTMLElement | null = null; // +++ Store the observed element +++
|
let observedElement: HTMLElement | null = null; // +++ Store the observed element +++
|
||||||
let debounceTimer: number | null = null; // 用于防抖的计时器 ID
|
let debounceTimer: number | null = null; // 用于防抖的计时器 ID
|
||||||
let selectionListenerDisposable: IDisposable | null = null; // +++ 提升声明并添加类型 +++
|
let selectionListenerDisposable: IDisposable | null = null; // +++ 提升声明并添加类型 +++
|
||||||
|
let lastResizeObserverWidth = 0;
|
||||||
|
let lastResizeObserverHeight = 0;
|
||||||
|
const RESIZE_THRESHOLD = 0.5; // px
|
||||||
|
|
||||||
|
|
||||||
const { isMobile } = useDeviceDetection(); // 设备检测
|
const { isMobile } = useDeviceDetection(); // 设备检测
|
||||||
@@ -117,6 +120,19 @@ const fitAndEmitResizeNow = (term: Terminal) => {
|
|||||||
console.log(`[Terminal ${props.sessionId}] Immediate resize emit:`, dimensions);
|
console.log(`[Terminal ${props.sessionId}] Immediate resize emit:`, dimensions);
|
||||||
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: dimensions });
|
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: dimensions });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- 稳定 customHtmlLayerRef 的尺寸 (确保在 DOM 更新后) ---
|
||||||
|
nextTick(() => {
|
||||||
|
if (customHtmlLayerRef.value && terminalRef.value) {
|
||||||
|
const newWidth = terminalRef.value.offsetWidth;
|
||||||
|
const newHeight = terminalRef.value.offsetHeight;
|
||||||
|
customHtmlLayerRef.value.style.width = `${newWidth}px`;
|
||||||
|
customHtmlLayerRef.value.style.height = `${newHeight}px`;
|
||||||
|
console.log(`[Terminal ${props.sessionId}] customHtmlLayerRef stabilized (in nextTick) after immediate fit to: ${newWidth}x${newHeight}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 使用 nextTick 确保 fit() 的效果已反映,再触发 resize
|
// 使用 nextTick 确保 fit() 的效果已反映,再触发 resize
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 再次检查终端实例是否仍然存在
|
// 再次检查终端实例是否仍然存在
|
||||||
@@ -276,21 +292,62 @@ onMounted(() => {
|
|||||||
if (terminalRef.value) {
|
if (terminalRef.value) {
|
||||||
observedElement = terminalRef.value;
|
observedElement = terminalRef.value;
|
||||||
resizeObserver = new ResizeObserver((entries) => {
|
resizeObserver = new ResizeObserver((entries) => {
|
||||||
// Only process if the terminal is active
|
if (!props.isActive || !terminal || !terminalRef.value) return;
|
||||||
if (!props.isActive || !terminal) return;
|
|
||||||
|
|
||||||
const entry = entries[0];
|
const entry = entries[0];
|
||||||
const { height, width } = entry.contentRect; // 获取宽度和高度
|
const { height: rectHeight, width: rectWidth } = entry.contentRect;
|
||||||
// console.log(`[Terminal ${props.sessionId}] ResizeObserver triggered. Size: ${width}x${height}, isActive: ${props.isActive}`);
|
const offsetW = terminalRef.value.offsetWidth;
|
||||||
if (height > 0 && width > 0) { // 确保宽度和高度都有效
|
const offsetH = terminalRef.value.offsetHeight;
|
||||||
|
|
||||||
|
// --- 阈值判断逻辑 ---
|
||||||
|
const widthChangedSignificantly = Math.abs(rectWidth - lastResizeObserverWidth) >= RESIZE_THRESHOLD;
|
||||||
|
const heightChangedSignificantly = Math.abs(rectHeight - lastResizeObserverHeight) >= RESIZE_THRESHOLD;
|
||||||
|
|
||||||
|
if (!widthChangedSignificantly && !heightChangedSignificantly) {
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] Size change below threshold (${RESIZE_THRESHOLD}px). rectWidth: ${rectWidth.toFixed(2)}, rectHeight: ${rectHeight.toFixed(2)}, lastWidth: ${lastResizeObserverWidth.toFixed(2)}, lastHeight: ${lastResizeObserverHeight.toFixed(2)}. Skipping fit.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] Size change AT or ABOVE threshold (${RESIZE_THRESHOLD}px). rectWidth: ${rectWidth.toFixed(2)}, rectHeight: ${rectHeight.toFixed(2)}, lastWidth: ${lastResizeObserverWidth.toFixed(2)}, lastHeight: ${lastResizeObserverHeight.toFixed(2)}. Proceeding with fit.`);
|
||||||
|
|
||||||
|
const roundedWidth = Math.round(rectWidth);
|
||||||
|
const roundedHeight = Math.round(rectHeight);
|
||||||
|
|
||||||
|
// 更新 lastResizeObserverWidth/Height 为取整后的值
|
||||||
|
lastResizeObserverWidth = roundedWidth;
|
||||||
|
lastResizeObserverHeight = roundedHeight;
|
||||||
|
// --- 阈值判断逻辑结束 ---
|
||||||
|
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] Triggered. Observed contentRect: ${rectWidth.toFixed(2)}w x ${rectHeight.toFixed(2)}h (rounded to ${roundedWidth}x${roundedHeight}). terminalRef offset: ${offsetW}w x ${offsetH}h.`);
|
||||||
|
if (entry.target && terminalRef.value) {
|
||||||
|
const targetBoundingClientRect = entry.target.getBoundingClientRect();
|
||||||
|
const terminalRefBoundingClientRect = terminalRef.value.getBoundingClientRect();
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] target.getBoundingClientRect(): ${targetBoundingClientRect.width.toFixed(2)}w x ${targetBoundingClientRect.height.toFixed(2)}h, top: ${targetBoundingClientRect.top.toFixed(2)}, left: ${targetBoundingClientRect.left.toFixed(2)}`);
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] terminalRef.getBoundingClientRect(): ${terminalRefBoundingClientRect.width.toFixed(2)}w x ${terminalRefBoundingClientRect.height.toFixed(2)}h, top: ${terminalRefBoundingClientRect.top.toFixed(2)}, left: ${terminalRefBoundingClientRect.left.toFixed(2)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rectHeight > 0 && rectWidth > 0) {
|
||||||
try {
|
try {
|
||||||
// *** 恢复:立即调用 fit() 来适应前端容器 ***
|
// console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] Before fitAddon.fit(). Current xterm_cols: ${terminal.cols}, xterm_rows: ${terminal.rows}`);
|
||||||
fitAddon?.fit();
|
fitAddon?.fit();
|
||||||
// 触发防抖的 resize 发送,通知后端潜在的尺寸变化
|
// console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] After fitAddon.fit(). New xterm_cols: ${terminal.cols}, xterm_rows: ${terminal.rows}`);
|
||||||
debouncedEmitResize(terminal);
|
|
||||||
|
// --- 稳定 customHtmlLayerRef 的尺寸 (确保在DOM更新后) ---
|
||||||
|
nextTick(() => {
|
||||||
|
if (customHtmlLayerRef.value) {
|
||||||
|
customHtmlLayerRef.value.style.width = `${roundedWidth}px`;
|
||||||
|
customHtmlLayerRef.value.style.height = `${roundedHeight}px`;
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] customHtmlLayerRef stabilized (in nextTick) after ResizeObserver fit to: ${roundedWidth}x${roundedHeight}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// --- 稳定 customHtmlLayerRef 尺寸结束 ---
|
||||||
|
|
||||||
|
debouncedEmitResize(terminal); // This will log the cols/rows after debouncing
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Fit addon resize failed (observer):", e);
|
console.warn(`[TerminalResizeObserver sessionId=${props.sessionId}] Fit addon or debouncedEmitResize failed:`, e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`[TerminalResizeObserver sessionId=${props.sessionId}] Skipped fit/emit due to zero height/width in contentRect. Rect: ${rectWidth.toFixed(2)}w x ${rectHeight.toFixed(2)}h`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Observe only if initially active (or becomes active later)
|
// Observe only if initially active (or becomes active later)
|
||||||
@@ -300,6 +357,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 监听 isActive prop 的变化
|
// 监听 isActive prop 的变化
|
||||||
watch(() => props.isActive, (newValue, oldValue) => {
|
watch(() => props.isActive, (newValue, oldValue) => {
|
||||||
console.log(`[Terminal ${props.sessionId}] isActive changed from ${oldValue} to ${newValue}`);
|
console.log(`[Terminal ${props.sessionId}] isActive changed from ${oldValue} to ${newValue}`);
|
||||||
@@ -379,7 +437,6 @@ onMounted(() => {
|
|||||||
if (newSelection && newSelection !== currentSelection) {
|
if (newSelection && newSelection !== currentSelection) {
|
||||||
currentSelection = newSelection;
|
currentSelection = newSelection;
|
||||||
navigator.clipboard.writeText(newSelection).then(() => {
|
navigator.clipboard.writeText(newSelection).then(() => {
|
||||||
// console.log('[Terminal] 文本已自动复制到剪贴板:', newSelection); // 可选:成功日志
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('[Terminal] 自动复制到剪贴板失败:', err);
|
console.error('[Terminal] 自动复制到剪贴板失败:', err);
|
||||||
// 可以在这里向用户显示一个短暂的错误提示
|
// 可以在这里向用户显示一个短暂的错误提示
|
||||||
@@ -757,27 +814,32 @@ const executeScriptsInElement = (container: HTMLElement) => {
|
|||||||
console.log('[Terminal] Script #${index + 1} re-inserted and old one removed.');
|
console.log('[Terminal] Script #${index + 1} re-inserted and old one removed.');
|
||||||
} else {
|
} else {
|
||||||
container.appendChild(newScript);
|
container.appendChild(newScript);
|
||||||
console.warn('[Terminal] Script #${index + 1} had no parent, appended to container directly.');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log('[Terminal] Finished processing scripts in custom HTML.');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Watch for changes in terminalCustomHTML and execute scripts
|
// Watch for changes in terminalCustomHTML and execute scripts
|
||||||
watch(terminalCustomHTML, (newHtmlContent) => {
|
watch(terminalCustomHTML, (newHtmlContent, oldHtmlContent) => {
|
||||||
// Always operate within nextTick to ensure v-html has updated the DOM
|
console.log(`[TerminalCustomHTML Watch sessionId=${props.sessionId}] Triggered. New HTML defined: ${!!newHtmlContent}, Old HTML defined: ${!!oldHtmlContent}. New === Old: ${newHtmlContent === oldHtmlContent}`);
|
||||||
|
// 仅当实际 HTML 字符串内容发生变化时才继续。
|
||||||
|
if (newHtmlContent === oldHtmlContent && oldHtmlContent !== undefined) { // Ensure initial undefined oldHtmlContent still proceeds if newHtmlContent exists
|
||||||
|
console.log(`[TerminalCustomHTML Watch sessionId=${props.sessionId}] HTML content is same as old and old was defined. Skipping script execution.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 始终在 nextTick 中操作,以确保 v-html 已更新 DOM
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const container = customHtmlLayerRef.value;
|
const container = customHtmlLayerRef.value;
|
||||||
if (container) {
|
if (container) {
|
||||||
if (newHtmlContent) {
|
if (newHtmlContent) {
|
||||||
console.log('[Terminal] terminalCustomHTML changed, processing new HTML content.');
|
// console.log('[Terminal] terminalCustomHTML 内容已更改,正在为脚本处理新的 HTML 内容。'); // 保留原始的简单日志(注释掉)
|
||||||
executeScriptsInElement(container);
|
executeScriptsInElement(container);
|
||||||
} else {
|
} else {
|
||||||
console.log('[Terminal] terminalCustomHTML cleared.');
|
// console.log('[Terminal] terminalCustomHTML 内容已清除,脚本将不被处理。'); // 保留原始的简单日志(注释掉)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user