// ---------- 令牌(由宿主写入 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);