fix(debug): 修复预览不实时更新、出错位置不生效、中文Unicode转义等问题,预览改为右侧弹出面板,增加字数统计

This commit is contained in:
augushong
2026-05-24 09:32:32 +08:00
parent 4668865b4f
commit 7312db717c
2 changed files with 100 additions and 36 deletions

3
.gitignore vendored
View File

@@ -48,4 +48,5 @@ extend/base/common/command/curd/migrate_output.php
/source/**/.vscode
.trae/documents/
/docker-dev/
/.sisyphus
/.sisyphus
/.omo

View File

@@ -322,6 +322,13 @@ if (!function_exists('echo_value')) {
margin-right: 0;
padding-right: 0;
}
#char-count {
font-size: 12px;
color: #888;
min-width: 40px;
text-align: center;
font-family: Consolas, monospace;
}
#debug-toolbar .toolbar-group label {
display: inline-flex;
align-items: center;
@@ -372,25 +379,55 @@ if (!function_exists('echo_value')) {
}
#markdown-preview {
display: none;
padding: 20px;
position: fixed;
top: 0;
right: 0;
width: 45%;
height: 100%;
background: #fff;
position: relative;
margin-top: 20px;
border: 1px solid #ddd;
border-radius: 4px;
border-left: 1px solid #ddd;
box-shadow: -4px 0 16px rgba(0,0,0,0.12);
z-index: 99998;
box-sizing: border-box;
overflow: hidden;
}
#markdown-preview .preview-header {
padding: 12px 20px;
background: #f8f9fa;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
font-weight: bold;
color: #333;
}
#markdown-preview .preview-header .close-btn {
cursor: pointer;
font-size: 20px;
color: #999;
background: none;
border: none;
padding: 0 4px;
line-height: 1;
}
#markdown-preview .preview-header .close-btn:hover {
color: #333;
}
#markdown-preview pre {
white-space: pre-wrap;
word-break: break-word;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 13px;
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
border: 1px solid #e8e8e8;
background: #fff;
padding: 20px;
margin: 0;
border: none;
border-radius: 0;
color: #333;
max-height: 600px;
height: calc(100% - 49px);
overflow: auto;
box-sizing: border-box;
}
</style>
</head>
@@ -517,6 +554,7 @@ if (!function_exists('echo_value')) {
<label data-dynamic="env" data-env-key="Server/Request Data"><input type="checkbox" data-key="env_server"> Server</label>
</span>
<span class="toolbar-group">
<span id="char-count" title="当前选中内容的字符数">0字</span>
<button id="btn-copy" onclick="copyToClipboard()">复制给AI</button>
<button id="btn-preview" onclick="togglePreview()">预览</button>
<button id="btn-path" onclick="togglePathMode()">路径:简略</button>
@@ -526,8 +564,12 @@ if (!function_exists('echo_value')) {
<!-- Hidden root path -->
<div id="root-path" style="display:none;"><?php echo \think\facade\App::getRootPath(); ?></div>
<!-- Markdown Preview -->
<!-- Markdown Preview (right-side slide-out panel) -->
<div id="markdown-preview">
<div class="preview-header">
<span>Markdown Preview</span>
<button class="close-btn" onclick="togglePreview()">&times;</button>
</div>
<pre id="markdown-content"></pre>
</div>
@@ -634,21 +676,24 @@ if (!function_exists('echo_value')) {
<?php
// Message
if (isset($message)) {
$md = "# \u7cfb\u7edf\u53d1\u751f\u9519\u8bef\n\n## \u9519\u8bef\u4fe1\u606f\n> " . strip_tags((string)$message) . "\n\n";
$md = "# 系统发生错误\n\n## 错误信息\n> " . strip_tags((string)$message) . "\n\n";
echo 'mdFragments.message = ' . json_encode($md, JSON_UNESCAPED_UNICODE) . ";\n";
}
// Location
if (isset($file) && isset($line)) {
$fileEscaped = str_replace('\\', '\\\\', $file);
$md = "## \u51fa\u9519\u4f4d\u7f6e\n- **File:** `{$fileEscaped}`\n- **Line:** `{$line}`\n\n";
echo 'mdFragments.location = ' . json_encode($md, JSON_UNESCAPED_UNICODE) . ";\n";
// Location (from first trace)
if (isset($traces) && is_array($traces) && !empty($traces)) {
$firstTrace = $traces[0];
if (isset($firstTrace['file']) && isset($firstTrace['line'])) {
$fileEscaped = str_replace('\\', '\\\\', $firstTrace['file']);
$md = "## 出错位置\n- **File:** `{$fileEscaped}`\n- **Line:** `{$firstTrace['line']}`\n\n";
echo 'mdFragments.location = ' . json_encode($md, JSON_UNESCAPED_UNICODE) . ";\n";
}
}
// Call Stack (per trace)
if (isset($traces) && is_array($traces)) {
foreach ($traces as $index => $trace) {
$md = "## \u5f02\u5e38 #{$index}\n";
$md = "## 异常 #{$index}\n";
if (isset($trace['message'])) {
$md .= "**Message:** " . strip_tags($trace['message']) . "\n\n";
}
@@ -656,12 +701,12 @@ if (isset($traces) && is_array($traces)) {
$md .= "**Location:** `{$trace['file']}` : {$trace['line']}\n\n";
}
if (!empty($trace['trace'])) {
$md .= "### \u8c03\u7528\u6808\n";
$md .= "### 调用栈\n";
$count = 0;
$total = count($trace['trace']);
foreach ($trace['trace'] as $i => $item) {
if ($count >= 20) {
$md .= "... (\u5171 {$total} \u5c42\uff0c\u5df2\u622a\u65ad)\n";
$md .= "... ( {$total} 层,已截断)\n";
break;
}
$func = $item['function'] ?? 'unknown';
@@ -684,7 +729,7 @@ if (isset($traces) && is_array($traces)) {
if (isset($traces) && is_array($traces)) {
foreach ($traces as $index => $trace) {
if (!empty($trace['source'])) {
$md = "### \u6e90\u7801\u7247\u6bb5\n```php\n";
$md = "### 源码片段\n```php\n";
foreach ((array) $trace['source']['source'] as $key => $value) {
$lineNum = $key + $trace['source']['first'];
$marker = ($trace['line'] === $lineNum) ? ' >>>' : '';
@@ -699,7 +744,7 @@ if (isset($traces) && is_array($traces)) {
// Exception Data
if (isset($datas) && is_array($datas)) {
$hasData = false;
$md = "## \u5f02\u5e38\u6570\u636e\n";
$md = "## 异常数据\n";
foreach ($datas as $label => $value) {
if (!empty($value)) {
$hasData = true;
@@ -761,10 +806,14 @@ if (isset($tables) && is_array($tables)) {
}
restoreCheckboxState();
updateDisabledState();
// Auto-save on checkbox change
updatePreviewIfOpen();
// Auto-save on checkbox change + update preview if open
var checkboxes = document.querySelectorAll('#debug-toolbar input[type="checkbox"]');
checkboxes.forEach(function(cb) {
cb.addEventListener('change', saveCheckboxState);
cb.addEventListener('change', function() {
saveCheckboxState();
updatePreviewIfOpen();
});
});
}
@@ -807,6 +856,19 @@ if (isset($tables) && is_array($tables)) {
} catch(e) {}
}
// Update preview content if preview panel is currently open
function updatePreviewIfOpen() {
var preview = document.getElementById('markdown-preview');
var content = generateMarkdown();
var countEl = document.getElementById('char-count');
if (countEl) {
countEl.textContent = content.length + '字';
}
if (preview && preview.style.display === 'block') {
document.getElementById('markdown-content').textContent = content;
}
}
// Update disabled state for env checkboxes based on data availability
function updateDisabledState() {
// Exception data
@@ -863,20 +925,20 @@ if (isset($tables) && is_array($tables)) {
window.copyToClipboard = function() {
var content = generateMarkdown();
if (!content.trim()) {
alert('\u8bf7\u81f3\u5c11\u52fe\u9009\u4e00\u4e2a\u5185\u5bb9\u9009\u9879');
alert('请至少勾选一个内容选项');
return;
}
var btn = document.getElementById('btn-copy');
if (navigator.clipboard) {
navigator.clipboard.writeText(content).then(function() {
btn.textContent = '\u5df2\u590d\u5236';
btn.textContent = '已复制';
btn.classList.add('copied');
setTimeout(function() {
btn.textContent = '\u590d\u5236\u7ed9AI';
btn.textContent = '复制给AI';
btn.classList.remove('copied');
}, 2000);
}, function(err) {
alert('\u590d\u5236\u5931\u8d25: ' + err);
alert('复制失败: ' + err);
});
} else {
var textarea = document.createElement('textarea');
@@ -887,14 +949,14 @@ if (isset($tables) && is_array($tables)) {
textarea.select();
try {
document.execCommand('copy');
btn.textContent = '\u5df2\u590d\u5236';
btn.textContent = '已复制';
btn.classList.add('copied');
setTimeout(function() {
btn.textContent = '\u590d\u5236\u7ed9AI';
btn.textContent = '复制给AI';
btn.classList.remove('copied');
}, 2000);
} catch (err) {
alert('\u590d\u5236\u5931\u8d25');
alert('复制失败');
}
document.body.removeChild(textarea);
}
@@ -909,11 +971,11 @@ if (isset($tables) && is_array($tables)) {
var content = generateMarkdown();
document.getElementById('markdown-content').textContent = content;
preview.style.display = 'block';
btn.textContent = '\u5173\u95ed\u9884\u89c8';
btn.textContent = '关闭预览';
btn.classList.add('active');
} else {
preview.style.display = 'none';
btn.textContent = '\u9884\u89c8';
btn.textContent = '预览';
btn.classList.remove('active');
}
};
@@ -923,13 +985,14 @@ if (isset($tables) && is_array($tables)) {
var btn = document.getElementById('btn-path');
if (currentPathMode === 'simple') {
currentPathMode = 'full';
btn.textContent = '\u8def\u5f84:\u5b8c\u6574';
btn.textContent = '路径:完整';
btn.classList.add('active');
} else {
currentPathMode = 'simple';
btn.textContent = '\u8def\u5f84:\u7b80\u7565';
btn.textContent = '路径:简洁';
btn.classList.remove('active');
}
updatePreviewIfOpen();
};
})();
</script>