Cosenseのページ内の画像を拡大表示したとき、アイコンクリックやキー操作で、前の画像/次の画像に遷移するUserScript
機能
Cosenseのページ内に掲載されている画像をクリックして拡大表示したとき、画像の左右に‹と›のアイコンを表示します。
‹をクリックするか、キーボードの左キーを押すと、〈前の画像=上の行に掲載されている画像〉に遷移します。
›をクリックするか、キーボードの右キーを押すと、〈次の画像=下の行に掲載されている画像〉に遷移します。
同じ行に2枚以上の画像が掲載されているときは、より左側にある画像を先頭側=前側として遷移します。
スクリーンショット
https://gyazo.com/96e15ca603d490c0bc3b2b2e4bb3fb99
※アイコンの形や色は変更可能です(コードの冒頭部分で書き換える)。
注意点
画像のURLのドメインがgyazo.comでないときは、うまく動かない場合があるかもしれません。
将来のCosenseの仕様変更によって、正しく動かなくなることがあり得ます。
コード
コードはChatGPTに作らせたものです。
/help-jp/UserScriptの説明に従って導入し、ブラウザを1回リロードして下さい。
code:script.js
(() => {
'use strict';
const PREV_ICON = '‹'; /* 左へ=前の画像へ遷移するアイコン(変更可能) */
const NEXT_ICON = '›'; /* 右へ=次の画像へ遷移するアイコン(変更可能) */
/****************************************************************
* Gyazo URL を「IDまで」に正規化
****************************************************************/
function normalizeGyazoBase(url) {
return url.replace(
/(https:\/\/gyazo\.com\/0-9a-f+)(\/.*)?$/i,
'$1'
);
}
/****************************************************************
* ページ内画像の「正規化 base」を行順で取得
****************************************************************/
function collectImageBases() {
const bases = [];
document.querySelectorAll('.line a[href^="https://gyazo.com/"]').forEach(a => {
bases.push(normalizeGyazoBase(a.href));
});
return bases;
}
/****************************************************************
* 現在表示中画像の index を取得
****************************************************************/
function getCurrentIndex(modal) {
const img = modal.querySelector('img.full-content');
if (!img) return -1;
const bases = modal._cosenseBases;
if (!bases || !bases.length) return -1;
return bases.indexOf(normalizeGyazoBase(img.src));
}
/****************************************************************
* ナビ表示制御
****************************************************************/
function updateNavVisibility(modal) {
const index = getCurrentIndex(modal);
if (index === -1) return;
const { _cosensePrevBtn: prevBtn, _cosenseNextBtn: nextBtn } = modal;
const last = modal._cosenseBases.length - 1;
if (prevBtn) prevBtn.style.display = index > 0 ? '' : 'none';
if (nextBtn) nextBtn.style.display = index < last ? '' : 'none';
}
/****************************************************************
* モーダルにナビゲータ追加
****************************************************************/
function ensureNavigator(modal) {
if (!modal._cosenseBases) {
modal._cosenseBases = collectImageBases();
}
if (modal.querySelector('.cosense-image-nav')) {
updateNavVisibility(modal);
return;
}
const nav = document.createElement('div');
nav.className = 'cosense-image-nav';
nav.style.cssText = `
position: absolute;
top: 50%;
left: 0;
right: 0;
display: flex;
align-items: center;
pointer-events: none;
transform: translateY(-50%);
font-size: 40px;
color: white;
text-shadow: 0 0 6px black;
user-select: none;
`;
const mkBtn = (text, side) => {
const b = document.createElement('div');
b.textContent = text;
b.style.cssText = `
padding: 0 24px;
cursor: pointer;
pointer-events: auto;
line-height: 1;
${side === 'left' ? 'margin-right:auto;' : ''}
${side === 'right' ? 'margin-left:auto;' : ''}
`;
return b;
};
const prevBtn = mkBtn(PREV_ICON, 'left');
const nextBtn = mkBtn(NEXT_ICON, 'right');
nav.append(prevBtn, nextBtn);
modal.appendChild(nav);
modal._cosensePrevBtn = prevBtn;
modal._cosenseNextBtn = nextBtn;
prevBtn.onclick = e => {
e.stopPropagation();
navigate(modal, -1);
};
nextBtn.onclick = e => {
e.stopPropagation();
navigate(modal, +1);
};
updateNavVisibility(modal);
}
/****************************************************************
* 画像遷移(形式維持)
****************************************************************/
function navigate(modal, delta) {
const img = modal.querySelector('img.full-content');
const link = modal.querySelector('a.link');
const textSpan = modal.querySelector('.text span');
if (!img || !link) return;
const bases = modal._cosenseBases;
const index = getCurrentIndex(modal);
if (index === -1) return;
const nextIndex = index + delta;
if (nextIndex < 0 || nextIndex >= bases.length) return;
const nextBase = basesnextIndex;
const currentSrc = img.src;
let nextSrc;
if (/\/raw(\.jpg)?$/.test(currentSrc)) {
nextSrc = ${nextBase}/raw.jpg;
} else if (/\/max_size\/\d+/.test(currentSrc)) {
const size = currentSrc.match(/max_size\/(\d+)/)1;
nextSrc = ${nextBase}/max_size/${size};
} else {
nextSrc = nextBase;
}
img.src = nextSrc;
link.href = nextSrc;
if (textSpan) textSpan.textContent = nextSrc;
updateNavVisibility(modal);
}
/****************************************************************
* キーボード操作(← →)
****************************************************************/
document.addEventListener('keydown', e => {
const modal = document.querySelector('.full-content-modal.in');
if (!modal) return;
if (e.key === 'ArrowLeft') {
e.preventDefault();
navigate(modal, -1);
} else if (e.key === 'ArrowRight') {
e.preventDefault();
navigate(modal, +1);
}
});
/****************************************************************
* モーダル監視
****************************************************************/
new MutationObserver(() => {
const modal = document.querySelector('.full-content-modal.in');
if (modal) ensureNavigator(modal);
}).observe(document.body, {
childList: true,
subtree: true
});
console.log('Cosense Image Navigator loaded');
})();
更新履歴
2025-12-25 公開
2025-12-26 アイコンを細く変更
/icons/hr.icon
#UserScript #Gyazo #画像