import { spawn } from 'node:child_process' import { existsSync, readdirSync } from 'node:fs' import { mkdir, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { app } from 'electron' import type { EditorFile, HostResult } from '../shared/host' const ROOT = join(tmpdir(), 'ghosteditor-docs') export function findWordcraft(): string | null { const fromEnv = process.env.WORDCRAFT_EXE if (fromEnv && existsSync(fromEnv)) return fromEnv const roots = [process.cwd(), app.getAppPath(), join(app.getAppPath(), '..'), join(app.getAppPath(), '../..')] for (const root of roots) { const found = findExe(join(root, '.wordcraft', 'app')) if (found) return found } return null } function findExe(dir: string): string | null { if (!existsSync(dir)) return null for (const entry of readdirSync(dir, { withFileTypes: true })) { const full = join(dir, entry.name) if (entry.isFile() && entry.name.toLowerCase() === 'wordcraft.exe') return full if (entry.isDirectory()) { const nested = join(full, 'wordcraft.exe') if (existsSync(nested)) return nested const deeper = findExe(full) if (deeper) return deeper } } return null } function safeRelative(id: string): string | null { const parts = id.replace(/\\/g, '/').split('/').filter(Boolean) if (parts.length === 0 || parts.some((part) => part === '..' || part.includes(':'))) return null return parts.join('/') } export async function openInWordcraft(files: EditorFile[], activeId: string): Promise { const exe = findWordcraft() if (!exe) return { ok: false, error: 'WordCraft не найден' } const active = safeRelative(activeId) if (!active) return { ok: false, error: 'некорректный путь файла' } for (const file of files) { const relative = safeRelative(file.id) if (!relative) continue const path = join(ROOT, relative) await mkdir(dirname(path), { recursive: true }) await writeFile(path, file.text, 'utf8') } const child = spawn(exe, [join(ROOT, active)], { detached: true, stdio: 'ignore', windowsHide: false }) child.unref() return { ok: true } }