mcwiki

查看mc的wiki

适配设备:Pro

作者:r7a89600

下载量:6 · 点赞:0

可信源码风险提示:保存或下载前请阅读源码和权限声明。
SHA-256de43f6ac32569f2a8679dfbd9f7cf3e80ed8d92ea2e55c6d22d5146b5ec12658
const API = 'https://zh.minecraft.wiki/api.php';
const PAGE_CHARS = 800; // 每页800字,翻页看完全文

function req(url, timeout = 8000) {
  return new Promise((resolve, reject) => {
    const t = setTimeout(() => reject(new Error('超时')), timeout);
    system.http.request({
      url,
      method: 'GET',
      responseType: 'text',
      header: {
        'User-Agent': 'JSLab-WikiFull/1.0',
        'Api-User-Agent': 'JSLab-WikiFull/1.0',
        'Accept': 'application/json'
      },
      success(res) {
        clearTimeout(t);
        if (res.code === 204) return reject(new Error('空响应204'));
        if (res.code >= 200 && res.code < 300) {
          try { resolve(JSON.parse(res.data)); }
          catch (e) { reject(new Error('JSON解析失败')); }
        } else reject(new Error('HTTP ' + res.code));
      },
      fail(d, c) { clearTimeout(t); reject(new Error('fail:' + c)); }
    });
  });
}

// 轻度清洗:保留全文,只去最碍眼的东西
function cleanWiki(wt) {
  return wt
    .replace(/<nowiki>([\s\S]*?)<\/nowiki>/g, '$1')
    .replace(/<!--[\s\S]*?-->/g, '')
    .replace(/<ref>[\s\S]*?<\/ref>/g, '')      // 脚注不显示
    .replace(/<[^>]+>/g, '')
    .replace(/\[\[(?:[^\]|]*\|)?([^\]]+)\]\]/g, '$1') // 链接保留文字
    .replace(/'{2,}/g, '')
    .replace(/[ \t]+\n/g, '\n')
    .replace(/\n{3,}/g, '\n\n')
    .trim();
}

function paginate(text, size) {
  const pages = [];
  for (let i = 0; i < text.length; i += size) {
    pages.push(text.slice(i, i + size));
  }
  return pages.length ? pages : [''];
}

async function main() {
  const view = ui.signal('search');
  const results = ui.signal([]);
  const loading = ui.signal(false);
  const error = ui.signal('');

  const title = ui.signal('');
  const pages = ui.signal([]);
  const pageIdx = ui.signal(0);
  const fullLen = ui.signal(0);

  async function doSearch() {
    const kw = await dialog.text({ title: '搜 Wiki', placeholder: '石头 / 红石 / 活塞' });
    if (kw.action !== 'confirm') return;
    const k = kw.value.trim();
    if (!k) return;

    loading.set(true); error.set(''); results.set([]); ui.refresh();
    try {
      const u = API + '?action=query&list=search&srsearch='
        + encodeURIComponent(k) + '&srlimit=5&format=json';
      const d = await req(u);
      const list = (d.query?.search || []).map(x => ({
        title: x.title,
        snippet: (x.snippet || '').replace(/<[^>]+>/g, '')
      }));
      results.set(list);
      if (!list.length) error.set('没搜到,换关键词');
    } catch (e) {
      error.set('搜索失败: ' + e.message);
    }
    loading.set(false); ui.refresh();
  }

  async function openPage(t) {
    title.set(t);
    view.set('reading');
    pages.set(['加载全文中...']);
    pageIdx.set(0);
    ui.refresh();

    try {
      // 关键:拿完整 wikitext,不传 section,不剪枝
      const u = API + '?action=parse&page=' + encodeURIComponent(t)
        + '&prop=wikitext&format=json';
      const d = await req(u);
      const raw = d.parse?.wikitext?.['*'] || '';
      const cleaned = cleanWiki(raw);
      fullLen.set(cleaned.length);
      // 一字不落:全部切页
      pages.set(paginate(cleaned, PAGE_CHARS));
      pageIdx.set(0);
    } catch (e) {
      pages.set(['获取失败: ' + e.message]);
      fullLen.set(0);
    }
    ui.refresh();
  }

  function render() {
    if (view.get() === 'search') {
      const nodes = [
        ui.heading('MC Wiki 全文', { size: 20 }),
        ui.button(loading.get() ? '搜索中...' : '搜索', doSearch, { tone: 'primary' }),
        ui.spacer(6)
      ];
      if (error.get()) nodes.push(ui.text(error.get(), { size: 14, color: '#f66' }));
      results.get().forEach(it => {
        nodes.push(ui.button(it.title, () => openPage(it.title), { tone: 'neutral' }));
      });
      return ui.column(nodes, { gap: 4 });
    }

    const ps = pages.get();
    const idx = pageIdx.get();
    const total = ps.length;
    return ui.column([
      ui.button('← 返回', () => { view.set('search'); ui.refresh(); }, { tone: 'neutral' }),
      ui.spacer(4),
      ui.heading(title.get(), { size: 16, lines: 1 }),
      ui.text('全文共 ' + fullLen.get() + ' 字,当前第 ' + (idx + 1) + '/' + total + ' 页',
              { size: 12, color: '#888' }),
      ui.spacer(4),
      ui.text(ps[idx] || '', { size: 14, lineHeight: 20, lines: 16 }),
      ui.spacer(6),
      ui.buttonRow([
        { text: '上一页', onPress: () => { if (idx > 0) { pageIdx.set(idx - 1); ui.refresh(); } },
          tone: 'neutral', disabled: idx <= 0 },
        { text: '下一页', onPress: () => { if (idx < total - 1) { pageIdx.set(idx + 1); ui.refresh(); } },
          tone: 'neutral', disabled: idx >= total - 1 }
      ])
    ], { gap: 4 });
  }

  ui.render(render);
}

return main();