// ---------- 令牌(由宿主写入 URL fragment,最可靠) ---------- (function () { try { var m = (location.hash || '').match(/token=([A-Za-z0-9_-]+)/); if (m && m[1] && m[1] !== 'none') window.__PET_TOKEN = m[1]; } catch (e) {} })(); // ---------- Pet 桥接(插件可用) ---------- window.Pet = { _native: function () { return window.PetNative || null; }, _req: function (path, body) { var h = { 'Content-Type': 'application/json' }; if (window.__PET_TOKEN) h['X-Pet-Token'] = window.__PET_TOKEN; return fetch(path, { method: 'POST', headers: h, body: JSON.stringify(body || {}) }) .then(function (r) { return r.json(); }); }, say: function (text, durationMs) { var n = this._native(); if (n) { try { n.say(String(text || ''), Number(durationMs) || 6000); } catch (e) {} return Promise.resolve({ ok: true }); } return this._req('/api/broadcast', { text: text, duration: durationMs || 6000 }); }, notify: function (state, text, withMeme) { var n = this._native(); if (n) { try { n.notify(String(state || 'working'), String(text || '')); } catch (e) {} return Promise.resolve({ ok: true }); } return this._req('/api/task', { state: state, text: text, withMeme: withMeme !== false }); }, anim: function (opt) { opt = opt || {}; var n = this._native(); if (n) { try { n.anim(opt.tag || '', opt.name || '', !!opt.random); } catch (e) {} return Promise.resolve({ ok: true }); } return this._req('/api/anim', opt); }, meme: function (opt) { opt = opt || {}; var n = this._native(); if (n) { try { n.meme(opt.tag || ''); } catch (e) {} return Promise.resolve({ ok: true }); } return this._req('/api/meme', opt); }, skill: function (id, text) { var n = this._native(); if (n) { try { n.skill(String(id || '')); } catch (e) {} return Promise.resolve({ ok: true }); } return this._req('/api/skill', { id: id, text: text }); }, status: function () { var n = this._native(); if (n) { try { return Promise.resolve(JSON.parse(n.status())); } catch (e) {} } return fetch('/api/state').then(function (r) { return r.json(); }); }, on: function (evt, fn) { document.addEventListener('pet:' + evt, function (e) { fn(e.detail); }); }, log: function () { try { console.log.apply(console, arguments); } catch (e) {} } }; // 宿主直接把互动事件推进来 window.__petDispatch = function (type) { try { document.dispatchEvent(new CustomEvent('pet:' + type, { detail: { type: type } })); } catch (e) {} }; // ---------- 互动事件转发(Pet.on) ---------- function attachEvents() { try { var es = new EventSource('/api/events' + (window.__PET_TOKEN ? ('?token=' + window.__PET_TOKEN) : '')); es.addEventListener('pet', function (e) { try { var d = JSON.parse(e.data); document.dispatchEvent(new CustomEvent('pet:' + d.type, { detail: d })); if (d.type === 'anim' && d.name) setPet(d.name); } catch (err) {} }); es.onerror = function () { setTimeout(function () { try { es.close(); } catch (e) {} attachEvents(); }, 4000); }; } catch (e) {} } setTimeout(attachEvents, 800); // ---------- 插件加载 ---------- function loadPlugins() { fetch('/api/plugins.js').then(function (r) { return r.text(); }).then(function (code) { if (!code || !code.trim()) return; try { var fn = new Function('Pet', code); fn(window.Pet); console.log('[pet] plugins loaded'); } catch (e) { console.error('[pet] plugin error', e); } }).catch(function () {}); } setTimeout(loadPlugins, 600);