'
. '
'
. '' . esc_html($title) . ''
. '' . count($picked) . ''
. '
'
. '
'
. '
';
}
return $html;
}
/**
* the_content 主处理
*
* 顺序很重要:先给标题补 id(扫描出条目),再替换 [TOC]。
* 反过来的话,[TOC] 生成时拿不到标题清单。
*/
function yang_toc_filter_content($content)
{
if (is_admin() || !is_string($content) || $content === '') {
return $content;
}
if (stripos($content, '[toc') === false) {
// 没有 [TOC]:什么都不做。
// 不动标题 id 是刻意的 —— 不写 [TOC] 的文章不该被塞进一堆 id,
// 那会改变页面上已有的选择器和锚点行为。
return $content;
}
$scan = yang_toc_scan($content);
$items = $scan['items'];
/* 替换 [TOC] / [toc] / [TOC ...]。大小写都认 ——
用户不会记得"这里的标签必须大写",凭直觉写 [toc] 就该能用。 */
$out = preg_replace_callback(
'/\[toc(\s[^\]]*)?\]/i',
function ($m) use ($items) {
$atts = yang_toc_parse_atts(isset($m[1]) ? $m[1] : '');
return yang_toc_render($items, $atts);
},
$scan['content']
);
return is_string($out) ? $out : $content;
}
add_filter('the_content', 'yang_toc_filter_content', 9);
/**
* 短代码兜底
*
* 正常路径下 [TOC] 在优先级 9 就被替换掉了,走不到这里。
* 但如果在 the_content 之外被调用(小工具、页面构建器、do_shortcode()
* 显式调用),这条路径保证行为一致 —— 复用同一个渲染函数。
*/
function yang_toc_shortcode($atts = array(), $content = '', $tag = '')
{
$post = get_post();
if (!$post || !isset($post->post_content)) {
return '';
}
$scan = yang_toc_scan($post->post_content);
return yang_toc_render($scan['items'], is_array($atts) ? $atts : array());
}
add_shortcode('toc', 'yang_toc_shortcode');
add_shortcode('TOC', 'yang_toc_shortcode');
/**
* 需要时才加载样式
*
* 每篇文章都加一份 TOC 样式是浪费;只有正文里真的写了 [TOC] 才加载。
* 依赖 'kratos' 保证排在主题主样式之后,不会被覆盖。
*/
function yang_toc_assets()
{
if (is_admin() || !is_singular()) {
return;
}
$post = get_post();
if (!$post || !isset($post->post_content)) {
return;
}
if (!has_shortcode($post->post_content, 'toc') && !has_shortcode($post->post_content, 'TOC')) {
return;
}
wp_enqueue_style(
'yang-toc',
ASSET_PATH . '/assets/css/toc.css',
array('kratos'),
THEME_VERSION
);
wp_enqueue_script(
'yang-toc',
ASSET_PATH . '/assets/js/toc.js',
array(),
THEME_VERSION,
true
);
}
add_action('wp_enqueue_scripts', 'yang_toc_assets', 20);