Qiitaは3カラムレイアウトの真ん中のカラムにしかソースコードが表示されず、ウインドウ幅を広くしてもソースコード表示領域はあまり広がりません。
それでもあまり困らない場合が多かったのですが、最近はSwiftのソースコードをよく見るようになりもっと幅がほしくなったのでユーザースクリプトを作りました。
これを使ってQiitaのソースコード表示領域にマウスカーソルを乗せると、コピーボタンの横にボタンが増えます。
クリックするとウインドウ内のほぼ全面に、モーダルダイアログでソースコードを表示します。×ボタンやモーダルの外をクリックして閉じます。
ソースコード
// ==UserScript==
// @name Qiitaの記事中のソースコードをウインドウの幅で表示
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://qiita.com/*/items/*
// @grant none
// ==/UserScript==
(() => {
const style = `
#source-dialog {
background: #0008;
border: none;
position: fixed;
width: 100%;
height: 100%;
z-index: 999;
}
#source-dialog > .card {
background: #364549;
border-radius: .5rem;
height: 100%;
padding: 0 0 1.5rem 0;
width: 100%;
display: flex;
flex-direction: column;
}
#source-dialog > .card > .card-header {
text-align: right;
padding-bottom: .5rem;
}
#source-dialog > .card > .card-header > .close-button {
color: white;
}
#source-dialog > .card > .card-body {
flex-grow: 1;
height: 0;
}
#source-dialog > .card > .card-body > .code-frame {
height: 100%;
}
#source-dialog > .card > .card-body > .code-frame > .highlight {
height: 100%;
overflow-y: auto;
}
.code-copy {
right: 48px !important;
}
.code-open-dialog {
display: none
}
.hovering .code-open-dialog {
display: flex;
position: absolute;
transform: translateY(-8px);
font-size: 1.5rem;
}
.it-MdContent .code-open-dialog {
right: 32px;
}
.co-Item .code-open-dialog {
right: 16px;
}
.open-dialog {
color: #c8c8c8;
float: right;
padding: .5rem;
margin-top: -0.2rem;
}
.p-items_article i.open-dialog {
margin-right: -1.5rem;
}
`;
const element = document.createElement('style');
element.innerHTML = style;
document.head.appendChild(element);
document.body.insertAdjacentHTML('afterbegin', `
<dialog id="source-dialog">
<div class="card">
<div class="card-header">
<i class="fa fa-fw fa-close fa-lg close-button"></i>
</div>
<div class="card-body">
<div class="code-frame it-MdContent" data-lang=""></div>
</div>
</div>
</dialog>
`);
const wrapperElement = document.querySelector('.allWrapper');
const dialogElement = document.getElementById('source-dialog');
const cardElement = dialogElement.querySelector(':scope .card');
const contentElement = cardElement.querySelector(':scope .code-frame');
const closeButtonElement = cardElement.querySelector(':scope .close-button');
const showDialog = codeFrameElement => {
const language = codeFrameElement.getAttribute('data-lang');
const highlight = codeFrameElement.querySelector(':scope > .highlight');
const y = document.documentElement.scrollTop;
wrapperElement.style.top = String(-1 * y) + 'px';
wrapperElement.style.position = 'fixed';
wrapperElement.style.width = '100%';
wrapperElement.setAttribute('data-scroll-y', y);
contentElement.setAttribute('data-lang', language);
const clone = highlight.cloneNode(true);
contentElement.appendChild(clone);
dialogElement.setAttribute('open', 'open');
};
const closeDialog = () => {
wrapperElement.style.top = null;
wrapperElement.style.position = null;
wrapperElement.style.width = null;
const y = wrapperElement.getAttribute('data-scroll-y');
document.documentElement.scrollTop = y;
dialogElement.removeAttribute('open');
contentElement.innerHTML = '';
};
cardElement.addEventListener('click', e => e.stopPropagation());
closeButtonElement.addEventListener('click', _ => closeDialog());
dialogElement.addEventListener('click', _ => closeDialog());
const addIconToCodeFrame = codeFrame => {
const iconDiv = document.createElement('div');
iconDiv.className = 'code-open-dialog';
const hilightDiv = codeFrame.querySelector(':scope .highlight');
hilightDiv.insertAdjacentElement('beforebegin', iconDiv);
const iconElement = document.createElement('i');
iconElement.className = 'open-dialog fa fa-fw fa-external-link';
iconDiv.insertAdjacentElement('beforeend', iconElement);
iconElement.addEventListener('click', _ => showDialog(codeFrame));
codeFrame.addEventListener('mouseover', _ => {
codeFrame.classList.add('hovering');
});
codeFrame.addEventListener('mouseout', _ => {
codeFrame.classList.remove('hovering');
});
};
for(const codeFrame of document.querySelectorAll('.allWrapper .code-frame')) {
addIconToCodeFrame(codeFrame);
}
const commentsDiv = document.getElementById('comments');
new MutationObserver(records => {
records.forEach(record => {
for(const addedNode of record.addedNodes) {
if (addedNode.classList.contains('co-Item')) {
for(const codeFrame of addedNode.querySelectorAll(':scope .code-frame')) {
console.log('.codeframe', codeFrame);
addIconToCodeFrame(codeFrame);
}
}
}
});
}).observe(commentsDiv, {childList: true});
})();