Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion blog.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const BLOG = {
BEI_AN_LINK: process.env.NEXT_PUBLIC_BEI_AN_LINK || 'https://beian.miit.gov.cn/', // 备案查询链接,如果用了萌备等备案请在这里填写
BEI_AN_GONGAN: process.env.NEXT_PUBLIC_BEI_AN_GONGAN || '', // 公安备案号,例如 '浙公网安备3xxxxxxxx8号'

CODE_COLLAPSE_MIN_LINES: 30,//“长代码”阈值

// RSS订阅
ENABLE_RSS: process.env.NEXT_PUBLIC_ENABLE_RSS || true, // 是否开启RSS订阅功能

Expand Down Expand Up @@ -71,4 +73,4 @@ const BLOG = {
UUID_REDIRECT: process.env.UUID_REDIRECT || false
}

module.exports = BLOG
module.exports = BLOG
64 changes: 41 additions & 23 deletions components/PrismMac.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,50 @@ const renderCollapseCode = (codeCollapse, codeCollapseExpandDefault) => {
if (!codeCollapse) {
return
}

const COLLAPSE_MIN_LINES = Number(siteConfig('CODE_COLLAPSE_MIN_LINES', 18))
const codeBlocks = document.querySelectorAll('.code-toolbar')

for (const codeBlock of codeBlocks) {
// 判断当前元素是否被包裹
if (codeBlock.closest('.collapse-wrapper')) {
continue // 如果被包裹了,跳过当前循环
continue
}

const code = codeBlock.querySelector('code')
const language = code.getAttribute('class').match(/language-(\w+)/)[1]
if (!code) {
continue
}

const className = code.getAttribute('class') || ''
const languageMatch = className.match(/language-([\w-]+)/)
const language = languageMatch ? languageMatch[1] : ''

const text = code.textContent || ''
const lineCount = text ? text.split('\n').length : 0

// 方案 C:仅当代码行数超过阈值时才启用折叠
if (lineCount && lineCount < COLLAPSE_MIN_LINES) {
continue
}

const collapseWrapper = document.createElement('div')
collapseWrapper.className = 'collapse-wrapper w-full py-2'

const panelWrapper = document.createElement('div')
panelWrapper.className =
'border dark:border-gray-600 rounded-md hover:border-indigo-500 duration-200 transition-colors'
panelWrapper.className = 'collapse-panel-wrapper'

const header = document.createElement('button')
header.type = 'button'
header.className = 'collapse-header'

const label = language
? `${language.toUpperCase()} · ${lineCount} lines`
: `${lineCount} lines`

const header = document.createElement('div')
header.className =
'flex justify-between items-center px-4 py-2 cursor-pointer select-none'
header.innerHTML = `<h3 class="text-lg font-medium">${language}</h3><svg class="transition-all duration-200 w-5 h-5 transform rotate-0" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M6.293 6.293a1 1 0 0 1 1.414 0L10 8.586l2.293-2.293a1 1 0 0 1 1.414 1.414l-3 3a1 1 0 0 1-1.414 0l-3-3a1 1 0 0 1 0-1.414z" clip-rule="evenodd"/></svg>`
header.innerHTML = `<span class="collapse-label">${label}</span><svg class="collapse-chevron" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M6.293 6.293a1 1 0 0 1 1.414 0L10 8.586l2.293-2.293a1 1 0 0 1 1.414 1.414l-3 3a1 1 0 0 1-1.414 0l-3-3a1 1 0 0 1 0-1.414z" clip-rule="evenodd"/></svg>`

const panel = document.createElement('div')
panel.className =
'invisible h-0 transition-transform duration-200 border-t border-gray-300'
panel.className = 'collapse-panel'

panelWrapper.appendChild(header)
panelWrapper.appendChild(panel)
Expand All @@ -140,20 +160,18 @@ const renderCollapseCode = (codeCollapse, codeCollapseExpandDefault) => {
codeBlock.parentNode.insertBefore(collapseWrapper, codeBlock)
panel.appendChild(codeBlock)

function collapseCode() {
panel.classList.toggle('invisible')
panel.classList.toggle('h-0')
panel.classList.toggle('h-auto')
header.querySelector('svg').classList.toggle('rotate-180')
panelWrapper.classList.toggle('border-gray-300')
function setExpanded(expanded) {
panelWrapper.classList.toggle('is-expanded', expanded)
panel.classList.toggle('is-expanded', expanded)
header.setAttribute('aria-expanded', expanded ? 'true' : 'false')
}

// 点击后折叠展开代码
header.addEventListener('click', collapseCode)
// 是否自动展开
if (codeCollapseExpandDefault) {
header.click()
}
header.addEventListener('click', () => {
const expanded = panelWrapper.classList.contains('is-expanded')
setExpanded(!expanded)
})

setExpanded(Boolean(codeCollapseExpandDefault))
}
}

Expand Down
190 changes: 158 additions & 32 deletions public/css/prism-mac-style.css
Original file line number Diff line number Diff line change
@@ -1,58 +1,184 @@
/**
* @author https://github.com/txs
* 当配置文件 CODE_MAC_BAR 开启时,此样式会被动态引入,将开启代码组件左上角的mac图标
* 通用 Mac 风格代码块样式 (NotionNext Universal)
**/

/* 1. Mac 窗口容器样式 */
.code-toolbar {
position: relative;
padding-top: 0 !important;
padding-bottom: 0 !important;
width: 100%;
border-radius: 0.5rem;
margin-bottom: 0.5rem;
margin: 1rem 0;
border-radius: 14px;
border: 1px solid rgba(0, 0, 0, 0.1);
background: rgba(27, 28, 32, 0.94); /* 浅色模式下默认暗底 */
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 18px 44px rgba(0, 0, 0, 0.18);
overflow: hidden;
transition: box-shadow 0.3s ease, transform 0.3s ease;
}

.collapse-wrapper .code-toolbar {
margin-bottom: 0;
/* 暗色模式适配 */
html.dark .code-toolbar {
border-color: rgba(255, 255, 255, 0.12);
background: rgba(27, 28, 32, 0.72);
-webkit-backdrop-filter: saturate(140%) blur(12px);
backdrop-filter: saturate(140%) blur(12px);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 18px 44px rgba(0, 0, 0, 0.45);
}

.toolbar-item{
white-space: nowrap;
/* 2. Mac 三色点 */
.pre-mac {
position: absolute;
left: 12px;
top: 11px;
z-index: 13;
display: flex;
gap: 7px;
}

.toolbar-item > button {
margin-top: -0.1rem;
.pre-mac > span {
width: 10px;
height: 10px;
border-radius: 999px;
}

pre[class*='language-'] {
margin-top: 0rem !important;
// margin-bottom: 0rem !important;
padding-top: 1.5rem !important;
.pre-mac > span:nth-child(1) { background: #ff5f57; }
.pre-mac > span:nth-child(2) { background: #febc2e; }
.pre-mac > span:nth-child(3) { background: #28c840; }

/* 3. Toolbar 工具栏 (复制按钮、语言标签) */
.code-toolbar > .toolbar {
position: absolute;
top: 0;
right: 0;
height: 34px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 10px;
z-index: 12;
}

.pre-mac {
position: absolute;
left: 0.9rem;
top: 0.5rem;
z-index: 10;
.code-toolbar .toolbar-item > button {
font-size: 12px !important;
line-height: 1 !important;
padding: 6px 8px !important;
border-radius: 999px !important;
border: 1px solid rgba(255, 255, 255, 0.15) !important;
background: rgba(255, 255, 255, 0.1) !important;
color: rgba(255, 255, 255, 0.82) !important;
cursor: pointer;
transition: all 0.2s ease;
}

.pre-mac > span {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
float: left;
.code-toolbar .toolbar-item > button:hover {
background: rgba(255, 255, 255, 0.2) !important;
color: #fff !important;
}

/* 4. 代码正文排版 */
pre.notion-code {
font-size: 0.92em !important;
line-height: 1.6 !important;
margin: 0 !important;
padding: 46px 1.1rem 1rem !important;
border-radius: 0 !important;
border: none !important;
background: transparent !important;
color: rgba(255, 255, 255, 0.9) !important;
overflow: auto;
-webkit-overflow-scrolling: touch;
}

/* 5. 智能折叠 S1 极简 UI */
.collapse-wrapper {
margin: 1rem 0;
}

.pre-mac > span:nth-child(1) {
background: red;
.collapse-panel-wrapper {
border-radius: 14px;
border: 1px solid rgba(0, 0, 0, 0.08);
background: rgba(255, 255, 255, 0.55);
-webkit-backdrop-filter: saturate(160%) blur(10px);
backdrop-filter: saturate(160%) blur(10px);
overflow: hidden;
transition: all 0.3s ease;
}

.pre-mac > span:nth-child(2) {
background: sandybrown;
html.dark .collapse-panel-wrapper {
border-color: rgba(255, 255, 255, 0.12);
background: rgba(27, 28, 32, 0.6);
}

.pre-mac > span:nth-child(3) {
background: limegreen;
.collapse-header {
width: 100%;
height: 36px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
cursor: pointer;
user-select: none;
border: none;
background: transparent;
color: rgba(60, 60, 67, 0.6);
}

html.dark .collapse-header {
color: rgba(235, 235, 245, 0.6);
}

.collapse-label {
font-size: 13px;
letter-spacing: 0.02em;
}

.collapse-chevron {
width: 18px;
height: 18px;
transition: transform 0.3s ease;
opacity: 0.8;
}

.collapse-panel-wrapper.is-expanded .collapse-chevron {
transform: rotate(180deg);
}

.collapse-panel {
max-height: 0;
overflow: hidden;
border-top: 1px solid rgba(0, 0, 0, 0.06);
transition: max-height 0.32s ease;
}

html.dark .collapse-panel {
border-top-color: rgba(255, 255, 255, 0.08);
}

.collapse-panel.is-expanded {
max-height: 3000px;
}

/* 6. Prism 代码高亮补丁 (暗底优化) */
.code-toolbar .token.comment,
.code-toolbar .token.prolog,
.code-toolbar .token.doctype,
.code-toolbar .token.cdata { color: rgba(235, 235, 245, 0.46); }
.code-toolbar .token.punctuation { color: rgba(235, 235, 245, 0.6); }
.code-toolbar .token.property,
.code-toolbar .token.tag,
.code-toolbar .token.boolean,
.code-toolbar .token.number,
.code-toolbar .token.constant,
.code-toolbar .token.symbol,
.code-toolbar .token.deleted { color: #7ee787; }
.code-toolbar .token.selector,
.code-toolbar .token.attr-name,
.code-toolbar .token.string,
.code-toolbar .token.char,
.code-toolbar .token.builtin,
.code-toolbar .token.inserted { color: #a5d6ff; }
.code-toolbar .token.atrule,
.code-toolbar .token.attr-value,
.code-toolbar .token.keyword { color: #ff7ab2; }
.code-toolbar .token.function,
.code-toolbar .token.class-name { color: #ffd479; }