[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

80
lib/useRenderTracker.ts Normal file
View File

@@ -0,0 +1,80 @@
import { useRef } from "react";
import { logger } from "./logger";
// Set to true to enable render tracking logs (for debugging only)
const DEBUG_RENDER_TRACKING = false;
/**
* 追踪组件渲染次数和原因
* 在开发环境下帮助识别不必要的重渲染
*
* @param componentName 组件名称
* @param props 当前 props用于比较变化
* @param enabled 是否启用追踪,默认 false需要调试时手动启用
*/
export function useRenderTracker(
componentName: string,
props: Record<string, unknown>,
enabled: boolean = DEBUG_RENDER_TRACKING
): void {
const renderCountRef = useRef(0);
const prevPropsRef = useRef<Record<string, unknown>>({});
renderCountRef.current += 1;
if (!enabled) return;
const renderCount = renderCountRef.current;
const prevProps = prevPropsRef.current;
// 找出变化的 props
const changedProps: string[] = [];
const allKeys = new Set([...Object.keys(props), ...Object.keys(prevProps)]);
for (const key of allKeys) {
if (prevProps[key] !== props[key]) {
changedProps.push(key);
}
}
// 只在有变化时打印(减少日志噪音)
if (renderCount === 1) {
logger.info(`[Render] ${componentName} - 首次渲染`);
} else if (changedProps.length > 0) {
logger.info(`[Render] ${componentName} - 第${renderCount}次渲染`, {
changedProps,
details: changedProps.reduce((acc, key) => {
acc[key] = {
prev: summarizeValue(prevProps[key]),
curr: summarizeValue(props[key]),
};
return acc;
}, {} as Record<string, { prev: string; curr: string }>),
});
}
// 不再打印 "props未变化" 的警告 - 这是正常的 React 行为
// 更新 prevProps
prevPropsRef.current = { ...props };
}
/**
* 简化值的显示,避免日志过长
*/
function summarizeValue(value: unknown): string {
if (value === undefined) return "undefined";
if (value === null) return "null";
if (typeof value === "function") return `fn:${value.name || "anonymous"}`;
if (typeof value === "object") {
if (Array.isArray(value)) return `Array(${value.length})`;
const keys = Object.keys(value);
if (keys.length <= 3) {
return `{${keys.join(", ")}}`;
}
return `Object(${keys.length} keys)`;
}
if (typeof value === "string" && value.length > 30) {
return `"${value.slice(0, 30)}..."`;
}
return String(value);
}