tks
https://gyazo.com/433fdc7f465667137cb7a26ad600cf4a
https://gyazo.com/e5679737664c442ee1ee2d8fe5ae3b56
this is tks’s page
好きなこと
ラジオ・ポッドキャストを聴くこと
本を読む
関心事
知的生産、ナレッジマネジメント、情報管理
タスク管理
⏸️code:style.css
a.bidirectional-page-link{display:none}
ネタバレ防止
code:style.css
.deco-\#{ /* 記法のキーとして#を使ったけど何でもいいと思う */
background-color: #4a4a4a; /* 背景色をテキスト色と同じにする */ }
.line.cursor-line .deco-\#, .deco-\#:hover { /* カーソルがある行とマウスホバー時だけ背景を戻す */
background-color: inherit;
}
code:script.js
(function () {
// ボタンクリック時の処理
cosense.PageMenu.addMenu({
title: '見出し',
icon: 'fas fa-list',
onClick: generateHeadingMenu
})
// 見出しメニューを生成する関数
function generateHeadingMenu() {
try {
const menu = cosense.PageMenu('見出し')
menu.removeAllItems()
console.log('Generating heading menu...')
// すべての行をループ
for (let i = 0; i < cosense.Page.lines.length; i++) {
const line = cosense.Page.linesi // セクション開始でなければスキップ
if (!line.section || !line.section.start) continue
// 行のテキストを取得
const text = line.text || ''
// デバッグ: 各行のテキストを出力
if (text.length > 0) {
console.log(Checking line: "${text}")
}
let headingText = null
let headingLevel = 0
let prefixEmoji = null
// 1. 強調マークアップを探す
const strongMatch = text.match(strongRegex)
if (strongMatch && strongMatch1) { headingText = strongMatch1.trim() headingLevel = strongMatch0.match(/\*+/)0.length console.log(Found strong heading: "${headingText}" with level ${headingLevel})
}
// 2. 絵文字から始まる行を探す
else {
// 最初の文字を取得(トリムして空白を除去)
const trimmedText = text.trim()
if (trimmedText.length === 0) continue
// 最初の文字が絵文字かどうかをチェックする関数
function isFirstCharEmoji(text) {
// 指定された絵文字リスト(特に使用される絵文字)
const specificEmojis = ['📄', '📋', '✉️', '🤔', '🛒', '🗓️', '💡', '✅',
'☑️', '✓', '✔️', '📌', '📍', '🔍', '⭐', '★',
'☆', '⚡', '🔴', '🟠', '🟡', '🟢', '🔵', '🟣',
'⚫', '⚪', '📝', '📔', '📒', '📕', '📗', '📘',
'📙', '🔑', '🗝️', '👉', '👆', '☝️', '👇', '👈',
'❗', '❓', '‼️', '⁉️', '❕', '❔', '🚩', '🏁',
'🎯', '🔔', '🔖', '📑', '🛠️', '🔧', '🔨', '🗂️',
'📁', '📂', '💬', '🗣️', '📢', '📣'];
// テキストの最初の文字(絵文字は複数のコードポイントで構成されている可能性がある)
const firstTwoChars = text.slice(0, 2);
const firstThreeChars = text.slice(0, 3);
const firstFourChars = text.slice(0, 4);
// 明示的なリストチェック(最大4文字まで)
for (const emoji of specificEmojis) {
if (text.startsWith(emoji)) {
return emoji;
}
}
// 一般的な絵文字の範囲チェック(バックアップ)
const firstCharCode = text.codePointAt(0);
if ((firstCharCode >= 0x1F300 && firstCharCode <= 0x1F6FF) || // Miscellaneous Symbols and Pictographs
(firstCharCode >= 0x1F900 && firstCharCode <= 0x1F9FF) || // Supplemental Symbols and Pictographs
(firstCharCode >= 0x2600 && firstCharCode <= 0x26FF) || // Miscellaneous Symbols
(firstCharCode >= 0x2700 && firstCharCode <= 0x27BF) || // Dingbats
(firstCharCode >= 0x1F1E6 && firstCharCode <= 0x1F1FF) || // Flags
(firstCharCode >= 0x1F000 && firstCharCode <= 0x1F02F) || // Mahjong Tiles
(firstCharCode >= 0x1F0A0 && firstCharCode <= 0x1F0FF)) { // Playing Cards
// 絵文字の長さを判定
const firstCodePoint = String.fromCodePoint(firstCharCode);
return firstCodePoint;
}
return null; // 絵文字ではない
}
// 最初の文字が絵文字かチェック
const emoji = isFirstCharEmoji(trimmedText);
if (emoji) {
prefixEmoji = emoji;
// 絵文字の後のテキストを取得
const emojiLength = emoji.length;
let remainingText = trimmedText.slice(emojiLength).trim();
if (remainingText) {
headingText = remainingText;
headingLevel = 1; // 絵文字見出しは基本レベル1とする
console.log(Found emoji heading: "${headingText}" with emoji ${prefixEmoji});
}
}
}
// 見出しテキストが見つかった場合、メニューに追加
if (headingText) {
// クリックハンドラを定義
const lineId = line.id;
function jumpToLine() {
console.log('Jumping to line id:', lineId);
window.location.hash = lineId;
return false;
}
// メニュータイトルを作成
const menuTitle = prefixEmoji ? ${prefixEmoji} ${headingText} : headingText;
// メニューアイテムを追加
menu.addItem({
title: menuTitle,
onClick: jumpToLine
});
}
}
console.log('Heading menu generated successfully');
} catch (error) {
console.error('Error generating heading menu:', error);
}
}
})();
// 前日の日付にリンクする矢印アイコン
code:script.js
scrapbox.PageMenu.addMenu({
title: '前日',
onClick: () => {
const project, encodedTitle = location.pathname.split('/'); // プロジェクト名とページ名を取得
const title = decodeURIComponent(encodedTitle); // URLデコード
if (/^\d{4}\/\d{2}\/\d{2}$/.test(title)) { // ページ名が日付形式か確認(スラッシュ区切り)
// スラッシュをハイフンに変換してDateオブジェクトで処理
const dateForJs = title.replace(/\//g, '-');
const d = new Date(dateForJs); // 日付オブジェクトに変換
d.setDate(d.getDate() - 1); // 前日に設定
// yyyy-mm-dd形式で取得してからスラッシュ区切りに変換
const next = d.toISOString().slice(0, 10).replace(/-/g, '/'); // yyyy/mm/dd 形式に整形
// 出力時もURLエンコード
const nextEncoded = encodeURIComponent(next);
const a = document.createElement('a'); // <a> 要素を作成
Object.assign(a, {
href: /${project}/${nextEncoded}, // 遷移先のパスを指定(URLエンコード済み)
className: 'page-link', // Scrapbox 内部リンク用クラス
rel: 'route', // 内部ルーティングとして扱う
type: 'link' // リンクタイプ指定
});
a.dataset.pageTitle = next; // 遷移先のページタイトルをセット
document.body.append(a); // <body> に追加
a.click(); // 擬似的にクリックして遷移
a.remove(); // 使用後に要素を削除
}
}
});
// 翌日の日付にリンクする矢印アイコン
code:script.js
scrapbox.PageMenu.addMenu({
title: '翌日',
onClick: () => {
const project, encodedTitle = location.pathname.split('/'); // プロジェクト名とページ名を取得
const title = decodeURIComponent(encodedTitle); // URLデコード
if (/^\d{4}\/\d{2}\/\d{2}$/.test(title)) { // ページ名が日付形式か確認(スラッシュ区切り)
// スラッシュをハイフンに変換してDateオブジェクトで処理
const dateForJs = title.replace(/\//g, '-');
const d = new Date(dateForJs); // 日付オブジェクトに変換
d.setDate(d.getDate() + 1); // 翌日に設定
// yyyy-mm-dd形式で取得してからスラッシュ区切りに変換
const next = d.toISOString().slice(0, 10).replace(/-/g, '/'); // yyyy/mm/dd 形式に整形
// 出力時もURLエンコード
const nextEncoded = encodeURIComponent(next);
const a = document.createElement('a'); // <a> 要素を作成
Object.assign(a, {
href: /${project}/${nextEncoded}, // 遷移先のパスを指定(URLエンコード済み)
className: 'page-link', // Scrapbox 内部リンク用クラス
rel: 'route', // 内部ルーティングとして扱う
type: 'link' // リンクタイプ指定
});
a.dataset.pageTitle = next; // 遷移先のページタイトルをセット
document.body.append(a); // <body> に追加
a.click(); // 擬似的にクリックして遷移
a.remove(); // 使用後に要素を削除
}
}
});
CosenseでRINKシステムするUserScript
code:script.js
requestAnimationFrame(() => {
if (window.KCS?.role === 'SV') return;
cosense.on('lines:changed', ({ by }) => {
if (by === 'edit') updateKnowledgeReviewState();
});
async function updateKnowledgeReviewState () {
if (cosense.Layout !== 'page') return;
//ページのタイトルがナンバリングになっているかを確認する
if (!(/^\d{2}\./.test(cosense.Page.title)))return;
console.log("このページのタイトルは、二ケタの半角数字+.ではじまっています");
console.log("情報カードというリンクを持っています");
const matchedLink = cosense.Page.metadata?.links?.find(link =>
typeof link === "string" && /^\d{2}\./.test(link)
);
console.log("このページのリンクに二ケタの半角数字+.ではじまるページがあります");
if (!matchedLink) return;
const tempJSON = await fetchPageJSON(encodeURI(matchedLink))
const refpageList = tempJSON.relatedPages.links1hop
setRINK(refpageList)
return;
}
async function fetchPageJSON(encodedPageTitle) {
const url = https://scrapbox.io/api/pages/${scrapbox.Project.name}/${encodedPageTitle};
const response = await fetch(url);
if (!response.ok) {
throw new Error(${response.status} ${response.statusText});
}
const json = await response.json();
return json;
}
function setRINK(arry){
// 「二桁数字+ドット」で始まるタイトルだけに絞る
const filtered = arry.filter(obj => /^\d{2}\./.test(obj.title));
const titles = filtered.map(obj => obj.title).sort();
if (document.getElementById("tempRINKList")?.innerText == titles.join("\n")) {
const tempElem = document.getElementById("tempRINKList");
const sidebar = document.querySelector(".page-sidebar");
if (tempElem && tempElem.parentElement !== sidebar) {
sidebar.insertBefore(tempElem, sidebar.firstChild);
tempElem.style.position = "static"
}
return
}
const divBody = document.createElement("div")
divBody.id = "tempRINKList"
divBody.innerText = titles.join("\n")
const sidebar = document.querySelector(".page-sidebar")
if (sidebar){
sidebar.prepend(divBody)
divBody.style.position = "static"
}else{
const appContainer = document.querySelector(".app .container .page-column")
appContainer.append(divBody)
}
}
});
code:style.css
position: absolute;top: 72px;left: 20px;background: floralwhite;padding: 1em;
width:280px;border-radius: 5px;
}