0eba9e87e9
- Disable formatting rules (handled by Prettier) - Relaxed strict Vue/JS rules for demo code compatibility - Fix syntax errors in ApiPlayground and VoiceCloningDemo - Fix duplicate else-if condition in ApiPlayground - Fix Promise executor async pattern in AutoregressiveAudioDemo - Add TypeScript file support to ESLint config Warnings reduced from 295 to 251 problems. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
467 lines
10 KiB
Vue
467 lines
10 KiB
Vue
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
|
||
const codeLines = [
|
||
'function calculateTotal(price, tax) {',
|
||
' const taxAmount = price * tax;',
|
||
' const total = price + taxAmount;',
|
||
' return total;',
|
||
'}',
|
||
'',
|
||
'const myPrice = 100;',
|
||
'const myTax = 0.1;',
|
||
'const result = calculateTotal(myPrice, myTax);',
|
||
'console.log("Total:", result);'
|
||
]
|
||
|
||
const breakpoints = ref([2]) // Line 2 has breakpoint initially
|
||
const currentLine = ref(-1)
|
||
const isRunning = ref(false)
|
||
const variables = ref({})
|
||
const logs = ref([])
|
||
|
||
const toggleBreakpoint = (index) => {
|
||
const i = breakpoints.value.indexOf(index)
|
||
if (i === -1) {
|
||
breakpoints.value.push(index)
|
||
} else {
|
||
breakpoints.value.splice(i, 1)
|
||
}
|
||
}
|
||
|
||
const reset = () => {
|
||
currentLine.value = -1
|
||
isRunning.value = false
|
||
variables.value = {}
|
||
logs.value = []
|
||
}
|
||
|
||
const run = () => {
|
||
reset()
|
||
isRunning.value = true
|
||
step()
|
||
}
|
||
|
||
const step = () => {
|
||
if (!isRunning.value) return
|
||
|
||
let nextLine = currentLine.value + 1
|
||
|
||
// Skip empty lines
|
||
while (nextLine < codeLines.length && codeLines[nextLine].trim() === '') {
|
||
nextLine++
|
||
}
|
||
|
||
if (nextLine >= codeLines.length) {
|
||
isRunning.value = false
|
||
currentLine.value = -1
|
||
return
|
||
}
|
||
|
||
currentLine.value = nextLine
|
||
|
||
// Execute logic for simulation
|
||
updateVariables(nextLine)
|
||
|
||
// Check breakpoint
|
||
if (breakpoints.value.includes(nextLine)) {
|
||
// Paused
|
||
} else {
|
||
// Auto continue if no breakpoint, but for demo we might want manual stepping or slow motion
|
||
// For this demo, "Run" just goes to first breakpoint or end.
|
||
// But "Step" button is manual.
|
||
// Let's make "Run" auto-advance until breakpoint.
|
||
setTimeout(() => {
|
||
if (breakpoints.value.includes(nextLine)) {
|
||
// Pause
|
||
} else {
|
||
step()
|
||
}
|
||
}, 200) // Small delay to see execution
|
||
}
|
||
}
|
||
|
||
const stepOver = () => {
|
||
if (!isRunning.value && currentLine.value === -1) {
|
||
run()
|
||
return
|
||
}
|
||
|
||
// Force move to next line regardless of breakpoint
|
||
let nextLine = currentLine.value + 1
|
||
while (nextLine < codeLines.length && codeLines[nextLine].trim() === '') {
|
||
nextLine++
|
||
}
|
||
|
||
if (nextLine >= codeLines.length) {
|
||
isRunning.value = false
|
||
currentLine.value = -1
|
||
return
|
||
}
|
||
|
||
currentLine.value = nextLine
|
||
updateVariables(nextLine)
|
||
}
|
||
|
||
const updateVariables = (lineIndex) => {
|
||
// Simulation logic based on line number
|
||
// 0: function def
|
||
// 1: taxAmount = ... (inside function)
|
||
// 2: total = ... (inside function)
|
||
// 3: return
|
||
// 6: myPrice = 100
|
||
// 7: myTax = 0.1
|
||
// 8: call function
|
||
// 9: log
|
||
|
||
// We simulate the execution flow roughly
|
||
if (lineIndex === 6) variables.value = { ...variables.value, myPrice: 100 }
|
||
if (lineIndex === 7) variables.value = { ...variables.value, myTax: 0.1 }
|
||
|
||
// When calling function at line 8, we jump to line 0?
|
||
// This simple line-by-line is hard for function calls without complex logic.
|
||
// Let's simplify: Flatten the logic or just simulate state at specific lines.
|
||
|
||
if (lineIndex === 8) {
|
||
// Simulate jumping into function?
|
||
// For simplicity, let's just pretend we are inside.
|
||
// Or actually, let's just change the code to be flat for easier understanding in demo.
|
||
}
|
||
}
|
||
|
||
// Let's use a simpler flat code example for the demo to be robust
|
||
const flatCodeLines = [
|
||
'let count = 0;',
|
||
'const max = 3;',
|
||
'while (count < max) {',
|
||
' count = count + 1;',
|
||
' console.log("Count is:", count);',
|
||
'}',
|
||
'console.log("Done");'
|
||
]
|
||
// 0: let count = 0
|
||
// 1: const max = 3
|
||
// 2: while check
|
||
// 3: count++
|
||
// 4: log
|
||
// 5: } -> jump back to 2
|
||
// 6: log Done
|
||
|
||
// Re-implement step logic for flat code
|
||
const demoState = ref({
|
||
line: -1,
|
||
vars: { count: undefined, max: undefined },
|
||
output: [],
|
||
history: [] // to track loop
|
||
})
|
||
|
||
const flatStep = () => {
|
||
const s = demoState.value
|
||
let next = s.line
|
||
|
||
// Logic flow
|
||
if (s.line === -1) next = 0
|
||
else if (s.line === 0) next = 1
|
||
else if (s.line === 1) next = 2
|
||
else if (s.line === 2) {
|
||
// Check condition
|
||
if (s.vars.count < s.vars.max) next = 3
|
||
else next = 6
|
||
}
|
||
else if (s.line === 3) next = 4
|
||
else if (s.line === 4) next = 5
|
||
else if (s.line === 5) next = 2 // Loop back
|
||
else if (s.line === 6) {
|
||
// End
|
||
s.line = -1
|
||
isRunning.value = false
|
||
return
|
||
}
|
||
|
||
s.line = next
|
||
|
||
// Execute line
|
||
if (next === 0) s.vars.count = 0
|
||
if (next === 1) s.vars.max = 3
|
||
if (next === 3) s.vars.count++
|
||
if (next === 4) s.output.push(`Count is: ${s.vars.count}`)
|
||
if (next === 6) s.output.push('Done')
|
||
}
|
||
|
||
const flatRun = () => {
|
||
if (isRunning.value) return
|
||
demoState.value.line = -1
|
||
demoState.value.vars = { count: undefined, max: undefined }
|
||
demoState.value.output = []
|
||
isRunning.value = true
|
||
|
||
const tick = () => {
|
||
if (!isRunning.value) return
|
||
|
||
// Peek next line
|
||
// ... (Logic duplication is tricky, let's just use flatStep)
|
||
flatStep()
|
||
|
||
if (breakpoints.value.includes(demoState.value.line)) {
|
||
// Pause
|
||
} else if (demoState.value.line !== -1) {
|
||
setTimeout(tick, 500)
|
||
}
|
||
}
|
||
tick()
|
||
}
|
||
|
||
const flatResume = () => {
|
||
if (!isRunning.value) return
|
||
const tick = () => {
|
||
flatStep()
|
||
if (breakpoints.value.includes(demoState.value.line)) {
|
||
// Pause again
|
||
} else if (demoState.value.line !== -1) {
|
||
setTimeout(tick, 500)
|
||
}
|
||
}
|
||
setTimeout(tick, 500)
|
||
}
|
||
|
||
const flatNext = () => {
|
||
if (!isRunning.value && demoState.value.line === -1) {
|
||
demoState.value.vars = { count: undefined, max: undefined }
|
||
demoState.value.output = []
|
||
isRunning.value = true
|
||
}
|
||
flatStep()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<el-card
|
||
class="sources-demo"
|
||
shadow="hover"
|
||
>
|
||
<template #header>
|
||
<div class="header">
|
||
<span class="title">Sources (源代码调试)</span>
|
||
<div class="controls">
|
||
<el-button-group>
|
||
<el-button
|
||
type="success"
|
||
size="small"
|
||
icon="VideoPlay"
|
||
:disabled="isRunning && demoState.line !== -1 && !breakpoints.includes(demoState.line)"
|
||
@click="flatRun"
|
||
>
|
||
Run
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
icon="VideoPause"
|
||
:disabled="!breakpoints.includes(demoState.line)"
|
||
@click="flatResume"
|
||
>
|
||
Resume
|
||
</el-button>
|
||
<el-button
|
||
type="info"
|
||
size="small"
|
||
icon="ArrowRight"
|
||
@click="flatNext"
|
||
>
|
||
Step
|
||
</el-button>
|
||
</el-button-group>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<div class="container">
|
||
<div class="code-area">
|
||
<div
|
||
v-for="(line, index) in flatCodeLines"
|
||
:key="index"
|
||
class="line"
|
||
:class="{
|
||
active: demoState.line === index,
|
||
breakpoint: breakpoints.includes(index)
|
||
}"
|
||
@click="toggleBreakpoint(index)"
|
||
>
|
||
<div class="line-num">
|
||
{{ index + 1 }}
|
||
</div>
|
||
<div class="code-text">
|
||
{{ line }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar">
|
||
<div class="section">
|
||
<div class="section-title">
|
||
Scope (Variables)
|
||
</div>
|
||
<div class="var-list">
|
||
<div class="var-item">
|
||
<span class="name">count:</span>
|
||
<span class="value">{{ demoState.vars.count !== undefined ? demoState.vars.count : 'undefined' }}</span>
|
||
</div>
|
||
<div class="var-item">
|
||
<span class="name">max:</span>
|
||
<span class="value">{{ demoState.vars.max !== undefined ? demoState.vars.max : 'undefined' }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="section">
|
||
<div class="section-title">
|
||
Console Output
|
||
</div>
|
||
<div class="output-list">
|
||
<div
|
||
v-for="(log, i) in demoState.output"
|
||
:key="i"
|
||
class="log-line"
|
||
>
|
||
{{ log }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="footer-tip">
|
||
点击行号设置断点。点击 Run 开始执行,代码将在断点处暂停。
|
||
</div>
|
||
</el-card>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.sources-demo {
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.container {
|
||
display: flex;
|
||
height: 300px;
|
||
border: 1px solid #dcdfe6;
|
||
}
|
||
|
||
.code-area {
|
||
flex: 2;
|
||
background: #f5f7fa;
|
||
|
||
font-family: monospace;
|
||
border-right: 1px solid #dcdfe6;
|
||
}
|
||
|
||
.line {
|
||
display: flex;
|
||
cursor: pointer;
|
||
line-height: 24px;
|
||
}
|
||
|
||
.line:hover {
|
||
background-color: #ecf5ff;
|
||
}
|
||
|
||
.line.active {
|
||
background-color: #e8f3ff; /* Light blue background for execution line */
|
||
}
|
||
|
||
.line.active .code-text {
|
||
background-color: #cce5ff;
|
||
}
|
||
|
||
.line-num {
|
||
width: 40px;
|
||
text-align: right;
|
||
padding-right: 10px;
|
||
color: #909399;
|
||
border-right: 1px solid #ebeef5;
|
||
user-select: none;
|
||
position: relative;
|
||
}
|
||
|
||
.line.breakpoint .line-num::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 8px;
|
||
top: 6px;
|
||
width: 12px;
|
||
height: 12px;
|
||
background-color: #f56c6c;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* Green arrow for current line */
|
||
.line.active .line-num::after {
|
||
content: '→';
|
||
position: absolute;
|
||
right: 2px;
|
||
color: #409eff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.code-text {
|
||
padding-left: 10px;
|
||
white-space: pre;
|
||
color: #303133;
|
||
flex: 1;
|
||
}
|
||
|
||
.sidebar {
|
||
flex: 1;
|
||
background: #fff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.section {
|
||
padding: 10px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
}
|
||
|
||
.section-title {
|
||
font-weight: bold;
|
||
font-size: 12px;
|
||
color: #606266;
|
||
margin-bottom: 8px;
|
||
background: #f0f2f5;
|
||
padding: 4px;
|
||
}
|
||
|
||
.var-item {
|
||
font-family: monospace;
|
||
font-size: 13px;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.var-item .name {
|
||
color: #906fa5;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.var-item .value {
|
||
color: #409eff;
|
||
}
|
||
|
||
.output-list {
|
||
font-family: monospace;
|
||
font-size: 12px;
|
||
color: #606266;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.footer-tip {
|
||
margin-top: 10px;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
text-align: center;
|
||
}
|
||
</style>
|