可信源码风险提示:保存或下载前请阅读源码和权限声明。
const view = ui.signal('menu');
const title = ui.signal('');
const lines = ui.signal([]);
function req(url, parse) {
title.set('用户');
lines.set(['']);
view.set('result');
ui.refresh();
system.http.request({
url: url,
header: { 'User-Agent': 'JSLab/1.0' },
responseType: 'text',
success: response => {
try {
const d = JSON.parse(response.data);
if (d && d.message) {
lines.set(['GitHub 返回:', d.message.slice(0, 72)]);
} else {
lines.set(parse(d));
}
} catch (e) {
lines.set(['响应非 JSON:', response.data.slice(0, 80)]);
}
ui.refresh();
},
fail: (data, code) => {
lines.set(['请求失败: ' + code, String(data)]);
ui.refresh();
}
});
}
async function ask(cfg) {
const res = await dialog.text({
title: cfg.title,
message: cfg.message,
placeholder: cfg.placeholder,
maxLength: 60
});
if (res.action !== 'confirm') return;
const q = res.value.trim();
if (!q) return;
title.set(cfg.resultTitle + ' · ' + q);
req(cfg.url(q), cfg.parse);
}
async function queryUser() {
const res = await dialog.text({
title: '查询用户',
message: '输入 GitHub 用户名',
placeholder: '如 octocat',
maxLength: 40
});
if (res.action !== 'confirm') return;
const q = res.value.trim();
if (!q) return;
title.set('用户 · ' + q);
req('https://api.github.com/users/' + encodeURIComponent(q), d => {
return [
'登录名: ' + d.login,
'名字: ' + (d.name || '无'),
'仓库: ' + d.public_repos,
'粉丝: ' + d.followers + ' / 关注: ' + d.following,
'位置: ' + (d.location || '未知'),
'简介: ' + (d.bio || '无'),
'注册: ' + String(d.created_at || '').slice(0, 10)
];
});
}
async function queryRepo() {
const res = await dialog.text({
title: '查询仓库',
message: '输入 owner/repo 格式',
placeholder: '如 octocat/Hello-World',
maxLength: 100
});
if (res.action !== 'confirm') return;
const q = res.value.trim();
if (!q) return;
if (q.indexOf('/') < 0) {
await dialog.alert({ title: '格式错误', message: '应为 owner/repo 格式' });
return;
}
title.set('仓库 · ' + q);
req('https://api.github.com/repos/' + encodeURIComponent(q), d => {
return [
'仓库: ' + d.full_name,
'描述: ' + (d.description || '无'),
'语言: ' + (d.language || '未知'),
'Stars: ' + d.stargazers_count + ' / Forks: ' + d.forks_count,
'Issues: ' + d.open_issues_count,
'许可: ' + (d.license ? d.license.spdx_id : '无'),
'更新: ' + String(d.updated_at || '').slice(0, 10)
];
});
}
const SEARCH = {
code: {
title: '搜索代码',
message: '输入代码关键词',
placeholder: '如 accelerometer',
resultTitle: '代码',
url: q => 'https://api.github.com/search/code?q=' + encodeURIComponent(q) + '&per_page=5',
parse: d => d.items.slice(0, 5).map(it => [it.name, it.repository.full_name]).flat()
},
repo: {
title: '搜索仓库',
message: '输入仓库关键词',
placeholder: '如 watchface',
resultTitle: '仓库',
url: q => 'https://api.github.com/search/repositories?q=' + encodeURIComponent(q) + '&sort=stars&per_page=5',
parse: d => d.items.slice(0, 5).map(r => [
r.full_name + ' ★' + r.stargazers_count,
(r.language || '未知') + ' · ' + String(r.description || '').slice(0, 36)
]).flat()
},
issue: {
title: '搜索议题',
message: '输入议题关键词',
placeholder: '如 crash',
resultTitle: '议题',
url: q => 'https://api.github.com/search/issues?q=' + encodeURIComponent(q) + '&per_page=5',
parse: d => d.items.slice(0, 5).map(i => [
i.title.slice(0, 40),
(i.state === 'open' ? '开' : '关') + ' · ' + String(i.repository_url || '').split('/').pop()
]).flat()
},
pr: {
title: '搜索拉取请求',
message: '输入 PR 关键词',
placeholder: '如 fix crash',
resultTitle: 'PR',
url: q => 'https://api.github.com/search/issues?q=' + encodeURIComponent(q) + '+type:pr&per_page=5',
parse: d => d.items.slice(0, 5).map(p => [
'#' + p.number + ' ' + p.title.slice(0, 34),
(p.state === 'open' ? '开' : '关') + ' · ' + String(p.repository_url || '').split('/').pop()
]).flat()
},
org: {
title: '搜索组织',
message: '输入组织名',
placeholder: '如 openharmony',
resultTitle: '组织',
url: q => 'https://api.github.com/search/users?q=' + encodeURIComponent(q) + '+type:org&per_page=5',
parse: d => d.items.slice(0, 5).map(o => [
o.login,
String(o.description || '无简介').slice(0, 40)
]).flat()
}
};
function back() {
view.set('menu');
ui.refresh();
}
ui.setTitle('GitHub 查询工具');
ui.render(() => {
if (view.get() === 'result') {
return [
ui.button('← 返回', back, { id: 'b', tone: 'neutral', height: 40 }),
ui.spacer(8),
ui.heading(title.get(), { align: 'center' }),
ui.spacer(6)
].concat(lines.get().map((l, i) => ui.text(l, {
size: 13, color: i === 0 ? '#ffd75e' : (i % 2 === 1 ? '#bbbbbb' : '#ffffff'),
align: 'center', lines: 1, bold: i === 0
})));
}
return [
ui.heading('GitHub 查询', { align: 'center' }),
ui.row([
ui.button('查询用户', queryUser, { id: 'u', flex: 1, tone: 'primary', height: 52 }),
ui.button('查询仓库', queryRepo, { id: 'r', flex: 1, tone: 'success', height: 52 })
], { gap: 8 }),
ui.spacer(14),
ui.divider({ color: '#333333' }),
ui.spacer(10),
ui.text('搜索', { heading: true, align: 'center' }),
ui.row([
ui.button('代码', () => ask(SEARCH.code), { id: 'c', flex: 1, tone: 'warning', height: 46 }),
ui.button('仓库', () => ask(SEARCH.repo), { id: 'r2', flex: 1, tone: 'primary', height: 46 })
], { gap: 8 }),
ui.spacer(6),
ui.row([
ui.button('议题', () => ask(SEARCH.issue), { id: 'i', flex: 1, tone: 'success', height: 46 }),
ui.button('拉取请求', () => ask(SEARCH.pr), { id: 'p', flex: 1, tone: 'danger', height: 46 })
], { gap: 8 }),
ui.spacer(6),
ui.row([
ui.button('组织', () => ask(SEARCH.org), { id: 'o', flex: 1, tone: 'neutral', height: 46 })
], { gap: 8 })
];
});