refactor: simplify AI panel workflow - direct file modification and reload from disk

This commit is contained in:
2026-06-15 15:10:09 +08:00
parent 395c55b6b0
commit c1f7be1507
4 changed files with 20 additions and 87 deletions

View File

@@ -19,15 +19,9 @@ function toggleMode() {
function buildMessage(userInput: string): string {
if (mode.value !== 'json') return `需求: ${userInput}`
let ctx = `当前编辑文件: ${store.sourcePath}\n`
if (store.selectedScene) {
ctx += `选中场景: ${store.selectedScene.id}\n`
ctx += `当前场景 JSON:\n${JSON.stringify(store.selectedScene, null, 2)}\n\n`
} else if (Object.keys(store.gameData.scenes).length > 0) {
const { scenes, ...rest } = store.gameData
ctx += `全局配置:\n${JSON.stringify(rest, null, 2)}\n\n`
}
ctx += `需求: ${userInput}`
let ctx = `修改文件 ${store.sourcePath}`
if (store.selectedScene) ctx += `,针对场景节点 ${store.selectedScene.id}`
ctx += `。需求: ${userInput}`
return ctx
}
@@ -51,14 +45,8 @@ async function send() {
if (newSid) store.setAISessionId(newSid)
if (mode.value === 'json') {
const clean = result.replace(/^```json\n?|\n?```$/g, '').trim()
try {
JSON.parse(clean)
store.setAIResult(clean)
messages.value.push({ role: 'assistant', content: '已生成 JSON请查看编辑器面板' })
} catch {
messages.value.push({ role: 'assistant', content: result })
}
messages.value.push({ role: 'assistant', content: result || '已完成' })
await store.reloadFromDisk()
} else {
messages.value.push({ role: 'assistant', content: result || '已完成' })
}

View File

@@ -17,29 +17,6 @@ const store = useEditorStore()
const jsonText = ref('')
const errorMsg = ref('')
const saved = ref(false)
const showAcceptReject = ref(false)
const preAIValue = ref('')
watch(() => store.aiResult, (result) => {
if (!result) return
preAIValue.value = jsonText.value
jsonText.value = result
showAcceptReject.value = true
errorMsg.value = ''
})
function acceptAI() {
showAcceptReject.value = false
jsonText.value = jsonText.value
store.setAIResult('')
onBlur()
}
function rejectAI() {
jsonText.value = preAIValue.value
showAcceptReject.value = false
store.setAIResult('')
}
watch(() => [props.scene, store.gameData] as const, () => {
errorMsg.value = ''
@@ -79,11 +56,7 @@ function onBlur() {
<div class="editor-header">
<h3>{{ scene ? scene.id : '全局配置' }}</h3>
<div class="header-actions">
<span v-if="showAcceptReject" class="ai-actions">
<button class="ai-accept-btn" @click.stop="acceptAI">接受</button>
<button class="ai-reject-btn" @click.stop="rejectAI">撤销</button>
</span>
<span v-else-if="saved" class="saved-hint">已保存</span>
<span v-if="saved" class="saved-hint">已保存</span>
<span v-if="errorMsg" class="error-hint">JSON 错误: {{ errorMsg }}</span>
<button class="icon-btn" @click="store.showAIPanel = true" title="AI 助手">🤖</button>
<button v-if="scene" class="icon-btn danger" @click="emit('deleteScene', scene.id)" title="删除场景">🗑</button>
@@ -175,33 +148,6 @@ function onBlur() {
.icon-btn:hover { color: #ddd; border-color: rgba(255,255,255,0.25); }
.icon-btn.danger:hover { color: #e74c3c; border-color: #e74c3c; }
.ai-actions {
display: flex;
gap: 4px;
}
.ai-accept-btn {
padding: 3px 8px;
font-size: 11px;
color: #fff;
background: #4caf50;
border: none;
border-radius: 3px;
cursor: pointer;
}
.ai-accept-btn:hover { background: #388e3c; }
.ai-reject-btn {
padding: 3px 8px;
font-size: 11px;
color: #fff;
background: #e74c3c;
border: none;
border-radius: 3px;
cursor: pointer;
}
.ai-reject-btn:hover { background: #c62828; }
.json-area {
flex: 1;
padding: 14px 16px;

View File

@@ -10,7 +10,6 @@ export const useEditorStore = defineStore('editor', () => {
const sourcePath = ref('/scenes/demo.json')
const deepseekKey = ref(localStorage.getItem('deepseek_key') || '')
const showAIPanel = ref(false)
const aiResult = ref('')
const aiSessionId = ref('')
const selectedScene = computed(() => {
@@ -142,8 +141,6 @@ export const useEditorStore = defineStore('editor', () => {
function clearAISession() { aiSessionId.value = ''; localStorage.removeItem('editor_ai_session') }
function setAIResult(r: string) { aiResult.value = r }
async function autoSave() {
try {
await fetch('/api/save', {
@@ -154,11 +151,21 @@ export const useEditorStore = defineStore('editor', () => {
} catch { /* dev server not running */ }
}
async function reloadFromDisk() {
try {
const resp = await fetch(sourcePath.value)
const data = await resp.json()
gameData.value = data
selectedNodeId.value = null
clearAISession()
} catch { /* failed to reload */ }
}
return {
gameData, selectedNodeId, selectedScene, startSceneId, dirty, sourcePath,
deepseekKey, showAIPanel, aiResult, aiSessionId,
deepseekKey, showAIPanel, aiSessionId,
markDirty, loadJSON, exportJSON, addScene, deleteScene,
updateScene, addChoice, updateChoice, deleteChoice, generateId,
setSourcePath, setDeepseekKey, setAISessionId, clearAISession, setAIResult, autoSave,
setSourcePath, setDeepseekKey, setAISessionId, clearAISession, autoSave, reloadFromDisk,
}
})

View File

@@ -80,7 +80,7 @@ function apiSavePlugin() {
const modePrefix = mode === 'code'
? `当前项目根目录: ${__dirname}\n代码模式直接修改 src/ 下的源码文件并保存。需求:`
: `当前项目根目录: ${__dirname}\nJSON模式只返回修改后的 JSON 文本,不要写任何文件。需求:`
: `当前项目根目录: ${__dirname}\nJSON模式直接修改 JSON 配置文件并保存,用自然语言回复修改了什么。需求:`
const fullMessage = modePrefix + userMessage
const args = ['run', '--model', 'deepseek/deepseek-v4-pro', '--format', 'json']
@@ -131,16 +131,8 @@ function apiSavePlugin() {
}
if (mode === 'json') {
const codeBlock = aiText.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/)
let jsonStr = codeBlock ? codeBlock[1] : aiText
try {
JSON.parse(jsonStr)
} catch {
const bareMatch = aiText.match(/(\{[\s\S]*\}|\[[\s\S]*\])/)
jsonStr = bareMatch ? bareMatch[0] : aiText
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ result: jsonStr, sessionId: resolvedSessionId || '' }))
res.end(JSON.stringify({ result: aiText || 'done', sessionId: resolvedSessionId || '' }))
} else {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ result: aiText || 'done', sessionId: resolvedSessionId || '' }))