


.code-block {
position: relative;
margin: 1.5rem 0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
overflow: hidden;
z-index: 1;
background-color: #f5f5f5;
padding: 1rem;
border: 2px solid transparent;
}
@keyframes neon-border {
0% { border-color: #ff0080; box-shadow: 0 0 5px #ff0080; }
25% { border-color: #00ffff; box-shadow: 0 0 5px #00ffff; }
50% { border-color: #00ff00; box-shadow: 0 0 5px #00ff00; }
75% { border-color: #ffff00; box-shadow: 0 0 5px #ffff00; }
100% { border-color: #ff0080; box-shadow: 0 0 5px #ff0080; }
}
.code-block:hover:not([data-fullscreen="true"]) {
animation: neon-border 5s infinite;
transform: translateY(-3px) scale(1.02);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
z-index: 10;
}
.code-block pre {
margin: 0;
padding: 0;
}
.code-block-header .code-block-title {
font-weight: 500;
}
.theme-dark .code-block {
background-color: #1e1e1e;
color: #d4d4d4;
}
.code-block .protyle-action {
opacity: 0;
transition: opacity 0.3s ease;
}
.code-block:hover .protyle-action {
opacity: 1;
}
.code-block[data-fullscreen="true"] {
border: none;
animation: none;
transform: none !important;
box-shadow: none;
border-radius: 0;
margin: 0;
padding: 0;
}
.code-block[data-fullscreen="true"] .hljs {
height: calc(100vh - 45px);
padding: 1rem;
box-sizing: border-box;
}
.code-block[data-fullscreen="true"] .code-block-header {
padding: 8px 15px;
background-color: rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(127, 127, 127, 0.3);
}
.code-block[data-fullscreen="true"] .code-block-header {
border-left: 4px solid #ff0080;
transition: border-color 0.5s ease;
}
.code-block[data-fullscreen="true"] .code-block-header:hover {
border-left-color: #00ffff;
}
.code-fullscreen-button {
opacity: 0.6;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.code-fullscreen-button:hover {
opacity: 1;
transform: rotate(90deg);
}
.code-block.animate .hljs {
transition: max-height 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.code-arrow {
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@media (max-width: 768px) {
.code-block:hover:not([data-fullscreen="true"]) {
transform: translateY(-2px) scale(1.01);
}
.code-fullscreen-button {
right: 20px;
}
}
(() => {
const config = {
defaultTitle: "Code",
arrowExpandedHTML: '<svg class="code-arrow" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8.59 16.59L13.17 12L8.59 7.41L10 6L16 12L10 18L8.59 16.59Z"></path></svg>',
arrowCollapsedHTML: '<svg class="code-arrow" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8.59 16.59L13.17 12L8.59 7.41L10 6L16 12L10 18L8.59 16.59Z"></path></svg>',
fullscreenHTML: '<svg class="code-fullscreen" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"></path></svg>',
exitFullscreenHTML: '<svg class="code-fullscreen" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"></path></svg>',
headerMinWidth: 100,
collapseAnimation: true,
animationDuration: 300,
};
function addStyles() {
const styles = `
.code-block-header {
display: flex;
align-items: center;
padding: 4px 8px;
cursor: pointer;
background-color: var(--b3-code-block-bg);
color: var(--b3-protyle-inline-code-color);
border-bottom: 1px solid rgba(127, 127, 127, 0.2);
font-size: 13px;
font-family: var(--b3-font-family-code);
position: relative;
}
.code-block-title {
margin-left: 8px;
flex-grow: 1;
line-height: 1.5;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.code-arrow {
width: 35px;
height: 35px;
margin-left: 20px;
transition: transform 0.2s ease;
}
.code-fullscreen-button {
position: absolute;
right: 50px;
width: 35px;
height: 35px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0.7;
transition: all 0.3s ease;
}
.code-fullscreen-button:hover {
opacity: 1;
transform: scale(1.1);
}
.code-fullscreen {
width: 24px;
height: 24px;
}
.code-block-title:empty:before {
content: "${config.defaultTitle}";
opacity: 0.6;
}
.code-block[data-collapsed="true"] .code-block-header .code-arrow {
transform: rotate(0deg);
}
.code-block[data-collapsed="false"] .code-block-header .code-arrow {
transform: rotate(90deg);
}
.code-block[data-collapsed="true"] .code-block-header {
border-bottom: none;
opacity: 0.8;
}
.code-block-title:focus {
outline: none;
border-bottom: 1px dotted var(--b3-protyle-inline-code-color);
}
.code-block[data-collapsed="true"] .hljs {
display: none;
}
.code-block[data-fullscreen="true"] {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
background-color: var(--b3-code-block-bg);
display: flex;
flex-direction: column;
overflow: hidden;
}
.code-block[data-fullscreen="true"] .hljs {
flex-grow: 1;
overflow: auto;
max-height: none !important;
display: block !important;
height: auto !important;
opacity: 1 !important;
}
.code-block[data-fullscreen="true"] .code-block-header {
position: sticky;
top: 0;
z-index: 1;
}
`;
const styleEl = document.createElement('style');
styleEl.innerHTML = styles;
document.head.appendChild(styleEl);
}
function createCodeBlockHeader(codeBlock) {
if (codeBlock.querySelector('.code-block-header')) {
return;
}
const header = document.createElement('div');
header.className = 'code-block-header protyle-custom';
header.innerHTML = config.arrowExpandedHTML;
const title = document.createElement('div');
title.className = 'code-block-title';
title.contentEditable = 'true';
title.spellcheck = false;
const language = getCodeBlockLanguage(codeBlock);
if (language) {
title.textContent = language;
}
header.appendChild(title);
const fullscreenButton = document.createElement('div');
fullscreenButton.className = 'code-fullscreen-button';
fullscreenButton.innerHTML = config.fullscreenHTML;
fullscreenButton.title = "全屏显示代码";
header.appendChild(fullscreenButton);
header.addEventListener('click', (e) => {
if ((e.target === title && document.activeElement === title) ||
fullscreenButton.contains(e.target)) {
return;
}
toggleCodeBlock(codeBlock);
});
title.addEventListener('click', (e) => {
e.stopPropagation();
});
title.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
title.blur();
}
});
fullscreenButton.addEventListener('click', (e) => {
e.stopPropagation();
toggleFullscreen(codeBlock, fullscreenButton);
});
codeBlock.insertBefore(header, codeBlock.firstChild);
codeBlock.setAttribute('data-collapsed', 'false');
codeBlock.setAttribute('data-fullscreen', 'false');
}
function toggleFullscreen(codeBlock, button) {
const isFullscreen = codeBlock.getAttribute('data-fullscreen') === 'true';
const hljs = codeBlock.querySelector('.hljs');
if (isFullscreen) {
codeBlock.setAttribute('data-fullscreen', 'false');
button.innerHTML = config.fullscreenHTML;
button.title = "全屏显示代码";
if (codeBlock._wasCollapsedBeforeFullscreen) {
if (hljs) {
hljs.style.display = '';
hljs.style.maxHeight = '';
hljs.style.height = '';
hljs.style.opacity = '';
hljs.style.overflow = '';
delete hljs.dataset.animating;
}
setTimeout(() => {
codeBlock.setAttribute('data-collapsed', 'true');
if (hljs) {
hljs.style.display = 'none';
}
delete codeBlock._wasCollapsedBeforeFullscreen;
}, 50);
}
if (window._scrollPositionBeforeFullscreen !== undefined) {
window.scrollTo(0, window._scrollPositionBeforeFullscreen);
delete window._scrollPositionBeforeFullscreen;
}
} else {
window._scrollPositionBeforeFullscreen = window.scrollY;
if (codeBlock.getAttribute('data-collapsed') === 'true') {
codeBlock._wasCollapsedBeforeFullscreen = true;
codeBlock.setAttribute('data-collapsed', 'false');
if (hljs) {
hljs.style.display = 'block';
hljs.style.maxHeight = 'none';
hljs.style.height = 'auto';
hljs.style.opacity = '1';
hljs.style.overflow = 'auto';
if (hljs.dataset.animating === 'true') {
delete hljs.dataset.animating;
}
}
}
codeBlock.setAttribute('data-fullscreen', 'true');
button.innerHTML = config.exitFullscreenHTML;
button.title = "退出全屏";
setTimeout(() => {
if (hljs) {
hljs.scrollTop = 0;
}
}, 0);
}
}
function getCodeBlockLanguage(codeBlock) {
const hljsElement = codeBlock.querySelector('.hljs');
if (!hljsElement) return null;
const classes = hljsElement.className.split(' ');
for (const cls of classes) {
if (cls !== 'hljs' && cls.length > 0) {
return cls;
}
}
return null;
}
function toggleCodeBlock(codeBlock) {
if (codeBlock.getAttribute('data-fullscreen') === 'true') {
return;
}
const isCollapsed = codeBlock.getAttribute('data-collapsed') === 'true';
const hljs = codeBlock.querySelector('.hljs');
if (!hljs) return;
if (hljs.dataset.animating === 'true') {
return;
}
hljs.dataset.animating = 'true';
hljs.style.transition = 'none';
codeBlock.classList.remove('animate');
if (isCollapsed) {
hljs.style.display = 'block';
hljs.style.height = '0px';
hljs.style.overflow = 'hidden';
hljs.style.opacity = '0';
const targetHeight = hljs.scrollHeight;
const heightAnimation = hljs.animate([
{ height: '0px', opacity: 0 },
{ height: targetHeight + 'px', opacity: 1 }
], {
duration: 250,
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
fill: 'forwards'
});
heightAnimation.onfinish = () => {
hljs.style.height = '';
hljs.style.overflow = '';
hljs.style.opacity = '';
hljs.style.transition = '';
delete hljs.dataset.animating;
};
} else {
const startHeight = hljs.offsetHeight;
hljs.style.height = startHeight + 'px';
hljs.style.overflow = 'hidden';
const opacityAnimation = hljs.animate([
{ opacity: 1 },
{ opacity: 0 }
], {
duration: 120,
easing: 'ease-out',
fill: 'forwards'
});
setTimeout(() => {
const heightAnimation = hljs.animate([
{ height: startHeight + 'px' },
{ height: '0px' }
], {
duration: 180,
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
fill: 'forwards'
});
heightAnimation.onfinish = () => {
hljs.style.display = 'none';
hljs.style.height = '';
hljs.style.opacity = '';
hljs.style.overflow = '';
hljs.style.transition = '';
delete hljs.dataset.animating;
};
}, 70);
}
codeBlock.setAttribute('data-collapsed', !isCollapsed);
}
function processCodeBlocks(element) {
const codeBlocks = element.querySelectorAll('.code-block:not([data-processed="true"])');
codeBlocks.forEach(codeBlock => {
createCodeBlockHeader(codeBlock);
codeBlock.setAttribute('data-processed', 'true');
});
}
function observeCodeBlockAddition(container) {
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.classList && node.classList.contains('code-block')) {
createCodeBlockHeader(node);
node.setAttribute('data-processed', 'true');
}
if (node.querySelectorAll) {
processCodeBlocks(node);
}
}
});
}
});
});
observer.observe(container, {
childList: true,
subtree: true
});
return observer;
}
function setupKeyboardHandlers() {
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const fullscreenCodeBlock = document.querySelector('.code-block[data-fullscreen="true"]');
if (fullscreenCodeBlock) {
const fullscreenButton = fullscreenCodeBlock.querySelector('.code-fullscreen-button');
if (fullscreenButton) {
toggleFullscreen(fullscreenCodeBlock, fullscreenButton);
e.preventDefault();
}
}
}
});
}
function waitForElement(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(() => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
function isMobile() {
return !!document.getElementById("sidebar");
}
async function init() {
addStyles();
setupKeyboardHandlers();
const container = await waitForElement(isMobile() ? '.protyle-content' : '.layout__center');
processCodeBlocks(container);
observeProtyleAddition(container, protyles => {
protyles.forEach(protyle => {
if (!protyle.classList.contains('protyle')) {
protyle = protyle.closest('.protyle');
}
if (protyle) {
processCodeBlocks(protyle);
observeCodeBlockAddition(protyle);
}
});
});
}
function observeProtyleAddition(container, callback) {
const observer = new MutationObserver(mutations => {
const protyles = [];
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.classList &&
(node.classList.contains('protyle') ||
node.classList.contains('protyle-content'))) {
protyles.push(node);
}
}
});
}
});
if (protyles.length > 0) {
callback(protyles);
}
});
observer.observe(container, {
childList: true,
subtree: true
});
return observer;
}
init();
})();
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于