メモ帳を表示するPage Menu
どういうことか
「ここをみれば分かる」という場所がある
同様に、scrapboxでもやることを一覧できるようにするのが
の使い方にあっているのではないだろうか?
一定期間運用できていたという実績がある
仕様
指定したprojectから検索する
その行がリンクだけの場合は、そのリンク先ページを開くようにする
押すと該当ページに飛ぶ
新しいタブでいいか
reload
ボタンを押すと再読み込みが始まる
実装
他の方法
関連しないページ同士につけたくないなあ
こっちの方がよさそうではある
既存の道具の組み合わせで実現できる
毎日開くようにすれば、そのうちわからないことが出てきたらすぐそのページを開ける宵になるかもしれない
欠点
🔳つきページを一覧出来ない
これは専用のPage Menuを作らないとだめそうだなー
「Page Menuを毎日開け」という繰り返しタスクと組み合わせればいいのか
dependencies
script.jsimport {fetchPages, getFavicon} from '../scrapbox-api-helper/scrapboxAPI.js';
script.jsconst id = 'memorandum';
let initialized = false;
scrapbox.PageMenu.addMenu({
title: id,
image: `https://img.icons8.com/ios/180/${isDark() ? 'FFFFFF/' : ''}notepad.png`,
//image: `https://img.icons8.com/ios/180/FFFFFF/notepad.png`,
onClick: async () => {
if (initialized) return;
await reload();
initialized = true;
},
});
本体
script.jsconst memoPath = {project: 'takker-memex', title: 'メモ帳'};
let loading = false;
let timer = false;
async function reload() {
scrapbox.PageMenu(id).removeAllItems();
scrapbox.PageMenu(id).emitChange();
// 読み込みに時間がかかるようであれば、読み込み中の表示を出す
showLoading();
const pending = (async () => {
const items = await getMemos(memoPath.project, memoPath.title);
clearLoading();
items.forEach(({title, pathname, image}) =>
scrapbox.PageMenu(id).addItem({
title,
image,
onClick: () => window.open(`https://scrapbox.io${pathname}`),
})
);
})();
const pending2 = (async () => {
const items =
await getTaskItems(['takker-memex', 'takker'], ({title}) => title.startsWith('🔳'));
clearLoading();
items.forEach(({project, title, image}) => scrapbox.PageMenu(id).addItem({
title,
image,
onClick: () => window.open(`https://scrapbox.io/${project}/${title}`),
}));
})();
return await Promise.all([pending, pending2]);
}
条件に一致するページを取得する
script.jsasync function getTaskItems(projects, condition) {
return (await Promise.all(projects.map(async project => {
const image = await getFavicon(project);
return (await fetchPages({project}))
.flatMap(page => condition(page) ? [{project, title: page.title, image}] : []);
}))).flat();
}
特定のページからメモを取得する
[/icons/hr.icon]
から [/icons/hr.icon]
までを行IDつきで取得する
空行は無視する
script.jsasync function getMemos(project, pageTitle) {
const res = await fetch(`/api/pages/${project}/${pageTitle}`);
const {lines} = await res.json();
let start = false;
return lines.flatMap(({text, id}) => {
if (text === '[/icons/hr.icon]') {
start = !start;
return [];
}
if (!start) return [];
const title = text.trim();
if (title === '') return [];
const pathname = /^\[(?:[^=!"#%&'()*+,\-./{|}<>_~]|[=!"#%&'()*+,\-./{|}<>_~]\S)[^\]]+\]$/.test(title) ?
`/${project}/${title.match(/^\[([^\]]+)\]$/)[1]}` :
`/${project}/${pageTitle}#${id}`;
return [{
title,
pathname,
image: `https://img.icons8.com/ios/180/${isDark() ? 'FFFFFF/' : ''}unchecked-checkbox.png`,
}];
});
}
script.jsfunction showLoading() {
loading = true;
clearTimeout(timer);
timer = setTimeout(() => {
loading = true;
scrapbox.PageMenu(id).addItem({
title: 'Loading...',
image: `https://img.icons8.com/ios/180/${isDark() ? 'FFFFFF/' : ''}loading.png`,
onClick: () => {},
});
},
100,
);
}
function clearLoading() {
clearTimeout(timer);
if (loading) {
scrapbox.PageMenu(id).removeAllItems();
scrapbox.PageMenu(id).emitChange();
scrapbox.PageMenu(id).addItem({
title: 'Reload',
image: `https://img.icons8.com/ios/180/${isDark() ? 'FFFFFF/' : ''}refresh.png`,
onClick: () => reload(),
});
}
loading = false;
}
script.jsfunction isDark() {
return ['takker', 'takker-memex',].includes(scrapbox.Project.name);
}