/** Browser preview of the actual desktop HTML/CSS and controller, using fixture
* services only. Run: node scripts/preview-desktop-onboarding.mjs */
import { createServer } from 'node:http'
import { readFile } from 'node:fs/promises'
import { resolve, extname } from 'node:path'
const root = resolve(import.meta.dirname, '..')
const bridge = `
const flowReady = import('/desktop/onboarding.mjs').then(({DesktopOnboarding}) => {
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const flow = new DesktopOnboarding({
platform: 'darwin',
remoteServer: () => 'http://192.168.1.10:15300',
inspect: async () => { await delay(400); return {authenticated:false,registrationAvailable:true}; },
localSession: async () => ({authenticated:true,user:{username:'admin',role:'admin'}}),
register: async () => { await delay(400); },
prepareLocal: async () => {
for (const [stage,message] of [['checking','正在检查运行环境…'],['installing','正在准备服务组件…'],['starting','正在启动并检查服务…']]) {
flow.serviceChanged({phase:'starting',stage,message}); await delay(stage==='installing'?3000:1000);
}
return 'http://127.0.0.1:15300';
},
authenticate: async ({username,password}) => { await delay(600); if(password==='error')throw new Error('用户名或密码不正确'); return {username}; },
activate: async () => {},
navigate: async () => { document.querySelector('#session-loading-message').textContent='预览完成:正式应用将在这里进入工作区。'; },
});
flow.open();
return flow;
});
window.yaoyaoDesktop={status:()=>flowReady.then(f=>f.snapshot()),selectServer:mode=>flowReady.then(f=>f.select(mode)),prepareServer:input=>flowReady.then(f=>f.prepare(input)),login:input=>flowReady.then(f=>f.submit(input)),forceSync:()=>flowReady.then(f=>f.prepare()),logs:async()=>{document.getElementById('install-log').textContent+='\\n交互预览使用模拟服务,无本机安装日志。';}};
window.addEventListener('DOMContentLoaded',()=>{document.querySelector('#session-loading-message').textContent='交互预览 · 模拟服务';});
`
const types = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.mjs': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.svg': 'image/svg+xml' }
const server = createServer(async (req, res) => {
const path = new URL(req.url, 'http://localhost').pathname
if (path === '/preview-bridge.js') { res.setHeader('content-type', 'text/javascript'); res.end(bridge); return }
if (path === '/') { res.writeHead(302, { location: '/desktop/boot.html' }); res.end(); return }
const allowed = ['boot.html', 'boot.css', 'boot.js', 'boot-icons.svg', 'icon.png', 'onboarding.mjs', 'remote-login.mjs']
if (!allowed.some(file => path === '/desktop/' + file)) { res.writeHead(404); res.end(); return }
try {
let data = await readFile(resolve(root, '.' + path))
if (path.endsWith('boot.html')) data = data.toString().replace('