preview-desktop-onboarding.mjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. /** Browser preview of the actual desktop HTML/CSS and controller, using fixture
  2. * services only. Run: node scripts/preview-desktop-onboarding.mjs */
  3. import { createServer } from 'node:http'
  4. import { readFile } from 'node:fs/promises'
  5. import { resolve, extname } from 'node:path'
  6. const root = resolve(import.meta.dirname, '..')
  7. const bridge = `
  8. const flowReady = import('/desktop/onboarding.mjs').then(({DesktopOnboarding}) => {
  9. const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
  10. const flow = new DesktopOnboarding({
  11. remoteServer: () => 'http://192.168.1.10:15300',
  12. inspect: async () => { await delay(400); return {authenticated:false,setupRequired:flow.state.mode==='local'}; },
  13. prepareLocal: async () => {
  14. for (const [stage,message] of [['checking','正在检查运行环境…'],['installing','正在准备服务组件…'],['starting','正在启动并检查服务…']]) {
  15. flow.serviceChanged({phase:'starting',stage,message}); await delay(stage==='installing'?3000:1000);
  16. }
  17. return 'http://127.0.0.1:15300';
  18. },
  19. authenticate: async ({username,password}) => { await delay(600); if(password==='error')throw new Error('用户名或密码不正确'); return {username}; },
  20. activate: async () => {},
  21. navigate: async () => { document.querySelector('.intro h1').textContent='设置完成'; document.querySelector('.intro p').textContent='交互预览已完成,正式应用将在这里进入工作区。'; },
  22. });
  23. flow.open({mode:'remote',serverURL:'http://192.168.1.10:15300'});
  24. return flow;
  25. });
  26. 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交互预览使用模拟服务,无本机安装日志。';}};
  27. window.addEventListener('DOMContentLoaded',()=>{document.querySelector('.brand>span').textContent='交互预览 · 模拟服务';});
  28. `
  29. const types = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.mjs': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.svg': 'image/svg+xml' }
  30. const server = createServer(async (req, res) => {
  31. const path = new URL(req.url, 'http://localhost').pathname
  32. if (path === '/preview-bridge.js') { res.setHeader('content-type', 'text/javascript'); res.end(bridge); return }
  33. if (path === '/') { res.writeHead(302, { location: '/desktop/boot.html' }); res.end(); return }
  34. const allowed = ['boot.html', 'boot.css', 'boot.js', 'boot-icons.svg', 'icon.png', 'onboarding.mjs', 'remote-login.mjs']
  35. if (!allowed.some(file => path === '/desktop/' + file)) { res.writeHead(404); res.end(); return }
  36. try {
  37. let data = await readFile(resolve(root, '.' + path))
  38. if (path.endsWith('boot.html')) data = data.toString().replace('<script src="boot.js">', '<script src="/preview-bridge.js"></script><script src="boot.js">')
  39. res.setHeader('content-type', types[extname(path)]); res.end(data)
  40. } catch { res.writeHead(404); res.end() }
  41. })
  42. server.listen(Number(process.env.PORT || 4388), '127.0.0.1', () => console.log(`Desktop onboarding preview: http://127.0.0.1:${server.address().port}`))