2026-07-21 22:16:29 +08:00
|
|
|
import { syncActiveTree, switchDocumentTree } from './trees'
|
2026-07-21 20:09:00 +08:00
|
|
|
import type { BehaviorTreeDocument } from '../types'
|
|
|
|
|
|
|
|
|
|
export function serializeProject(document: BehaviorTreeDocument) {
|
2026-07-21 22:16:29 +08:00
|
|
|
return JSON.stringify(syncActiveTree(document), null, 2) + '\n'
|
2026-07-21 20:09:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseProject(content: string): BehaviorTreeDocument {
|
|
|
|
|
const value: unknown = JSON.parse(content)
|
|
|
|
|
if (!value || typeof value !== 'object') throw new Error('工程文件内容无效')
|
|
|
|
|
const candidate = value as Partial<BehaviorTreeDocument>
|
|
|
|
|
if (candidate.format !== 'arborflow/project' || candidate.version !== 1) {
|
|
|
|
|
throw new Error('不是受支持的 ArborFlow 工程文件')
|
|
|
|
|
}
|
|
|
|
|
if (!Array.isArray(candidate.nodes) || !Array.isArray(candidate.edges)) {
|
|
|
|
|
throw new Error('工程文件缺少节点或连接数据')
|
|
|
|
|
}
|
2026-07-21 22:16:29 +08:00
|
|
|
const document = candidate as BehaviorTreeDocument
|
|
|
|
|
const requestedTree = document.activeTreeId || document.mainTreeId
|
|
|
|
|
return document.trees?.length ? switchDocumentTree(document, requestedTree) : document
|
2026-07-21 20:09:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function downloadText(filename: string, content: string, type = 'text/plain') {
|
|
|
|
|
const blob = new Blob([content], { type })
|
|
|
|
|
const url = URL.createObjectURL(blob)
|
|
|
|
|
const anchor = document.createElement('a')
|
|
|
|
|
anchor.href = url
|
|
|
|
|
anchor.download = filename
|
|
|
|
|
anchor.click()
|
|
|
|
|
URL.revokeObjectURL(url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function pickBrowserFile(accept: string) {
|
|
|
|
|
return new Promise<{ path: string; content: string } | null>((resolve) => {
|
|
|
|
|
const input = document.createElement('input')
|
|
|
|
|
input.type = 'file'
|
|
|
|
|
input.accept = accept
|
|
|
|
|
input.onchange = async () => {
|
|
|
|
|
const file = input.files?.[0]
|
|
|
|
|
resolve(file ? { path: file.name, content: await file.text() } : null)
|
|
|
|
|
}
|
|
|
|
|
input.click()
|
|
|
|
|
})
|
|
|
|
|
}
|