首页/开发者文档/示例代码

示例代码

最小可运行骨架:启动幂等、存储兼容模板、锁定处理、导出与选图

示例代码

最小可运行骨架,覆盖本规范的全部必选项。可直接复制改造。

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>我的插件</title>
<style>
:root{--bg:#F5F1E8;--card:#FFF;--text:#3E3A33;--text2:#9B9484;--accent:#8B6F4E;--border:#E5DFD3}
@media(prefers-color-scheme:dark){:root{--bg:#191714;--card:#232019;--text:#EDE7DA;--text2:#8F887A;--border:#37322A}}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,-apple-system,'PingFang SC',sans-serif;background:var(--bg);color:var(--text);min-height:100vh}
.header{padding:12px 16px;padding-top:max(12px,env(safe-area-inset-top));border-bottom:1px solid var(--border)}
</style>
</head>
<body>
<div class="header">我的插件</div>
<div id="app"></div>
<script src="app.js"></script>
</body>
</html>

app.js

'use strict';
const $ = id => document.getElementById(id);

/* ===== 存储兼容模板(必须完整复制,详见开发指南) ===== */
function unwrapResp(r) {
  return (r && typeof r === 'object' && !Array.isArray(r) && ('value' in r)) ? r.value : r;
}
function parseStored(raw) {
  if (raw === null || raw === undefined || raw === '') return null;
  if (typeof raw === 'object') {
    if (raw && !Array.isArray(raw) && ('value' in raw)) raw = raw.value;
    if (raw === null || raw === undefined || raw === '') return null;
    if (typeof raw !== 'object') return parseStored(raw);
    return raw;
  }
  try { return JSON.parse(raw); } catch (e) { return null; }
}

/* ===== 启动(幂等:whenToolbox 与 toolboxReady 双触发安全) ===== */
function whenToolbox(timeoutMs) {
  return new Promise(function (resolve) {
    if (window.toolbox) return resolve(window.toolbox);
    var done = false;
    function ok() { if (done) return; done = true; resolve(window.toolbox || null); }
    try { window.addEventListener('yzhToolboxReady', ok, { once: true }); } catch (e) {}
    var waited = 0;
    var timer = setInterval(function () {
      waited += 100;
      if (window.toolbox || waited >= (timeoutMs || 3000)) { clearInterval(timer); ok(); }
    }, 100);
  });
}
function loadAll() {
  if (!window.toolbox || !window.toolbox.storage) {
    document.getElementById('app').textContent = '请在宇宙核 App 内使用本插件';
    return;
  }
  loadAllAsync();
}
async function loadAllAsync() {
  try {
    const data = parseStored(unwrapResp(await window.toolbox.storage.get('my_data')));
    render(Array.isArray(data) ? data : []);
  } catch (e) {
    // 任何启动异常都必须转成用户可见的错误,不允许静默卡死
    renderError(e);
  }
}
function showLocked(msg) { document.getElementById('app').textContent = msg; }

/* ===== 渲染与保存 ===== */
function render(items) {
  document.getElementById('app').textContent = items.length ? items.join('、') : '暂无内容';
}
async function saveData(items) {
  const res = await window.toolbox.storage.set('my_data', JSON.stringify(items));
  if (res && res.error) { renderError(res); return false; } // 错误必须可见
  return true;
}
function renderError(res) {
  const app = document.getElementById('app');
  app.textContent = res && res.locked
    ? '数据已加密,请解锁安全中心后重试'
    : '存储失败:' + (res && res.error || '未知错误');
}

/* ===== 事件绑定只做一次;启动双触发 ===== */
document.addEventListener('DOMContentLoaded', () => {
  document.getElementById('addBtn')?.addEventListener('click', async () => {
    const items = parseStored(unwrapResp(await window.toolbox.storage.get('my_data'))) || [];
    items.push('条目 ' + new Date().toLocaleTimeString());
    if (await saveData(items)) render(items);
  });
});
whenToolbox(3000).then(function () { loadAll(); });
window.addEventListener('toolboxReady', loadAll); // 存储水合完成后再读一次(幂等)

选图(image.pick,App 内唯一有效方式)

function pickImage(cb) { // cb(dataUrl)
  if (window.__yzhPreview) {            // 浏览器预览:降级 input[type=file]
    const inp = document.createElement('input');
    inp.type = 'file'; inp.accept = 'image/*';
    inp.onchange = () => { const f = inp.files[0]; if (f) readFile(f, cb); };
    inp.click();
    return;
  }
  window.toolbox.image.pick('gallery').then(r => {
    if (!r || !r.data) return;          // 用户取消
    cb('data:' + r.mimeType + ';base64,' + r.data);
  }).catch(e => alert('选图失败:' + (e.message || e)));
}
function readFile(file, cb) {
  const reader = new FileReader();
  reader.onload = () => cb(reader.result);
  reader.readAsDataURL(file);
}

导出(exportFile:写盘 + 自动弹系统分享)

async function doExport() {
  const res = await window.toolbox.file.exportFile(
    'export_' + Date.now() + '.txt',
    btoa(unescape(encodeURIComponent('导出内容')))); // UTF-8 文本转 base64
  if (res && res.error) { alert('导出失败:' + res.error); return; }
  alert('已导出,存储位置:' + (res && res.path || ''));
}

锁定处理(密码保护开启 + 安全中心锁定时)

storage 调用会返回 {error:'locked', locked:true}——显示锁定页 + 重试按钮, 重试 = 重新执行 loadAllAsync(解锁后即恢复):

function renderError(res) {
  if (res && res.locked) {
    document.getElementById('app').innerHTML =
      '<p>数据已加密,请先解锁安全中心。</p>' +
      '<button onclick="loadAllAsync()">重试</button>';
    return;
  }
  /* ...其他错误... */
}
MATRIX MODE: ON — 再输入一次或按 ESC 退出