]*>(.*?)<\/h\1>/i', $content, $matches, PREG_SET_ORDER ); // 見出しが2つ未満の場合は目次を生成しない if ( count( $matches ) < 2 ) { return $content; } // 見出しにIDを追加し、目次配列を生成 $toc_items = array(); foreach ( $matches as $index => $match ) { $level = $match[1]; $heading_text = strip_tags( $match[2] ); $heading_id = 'toc-' . ( $index + 1 ); // 見出しにIDを追加 $new_heading = str_replace( ' $level, 'text' => $heading_text, 'id' => $heading_id, ); } // 目次HTMLを生成 $toc_html = '
'; $toc_html .= '
'; $toc_html .= '' . esc_html__( '目次', 'ai-creative-studio' ) . ''; $toc_html .= ''; $toc_html .= '
'; $toc_html .= ''; $toc_html .= '
'; // 目次を挿入する位置を決定 $toc_position = get_post_meta( get_the_ID(), '_aics_toc_position', true ); $insert_at_h2 = 0; // デフォルトは最初のH2 switch ( $toc_position ) { case 'second': $insert_at_h2 = 1; break; case 'third': $insert_at_h2 = 2; break; case 'fourth': $insert_at_h2 = 3; break; case 'fifth': $insert_at_h2 = 4; break; default: $insert_at_h2 = 0; } // H2見出しを検索して目次を挿入 preg_match_all( '/]*>/i', $content, $h2_matches, PREG_OFFSET_CAPTURE ); if ( isset( $h2_matches[0][ $insert_at_h2 ] ) ) { $insert_position = $h2_matches[0][ $insert_at_h2 ][1]; $content = substr_replace( $content, $toc_html, $insert_position, 0 ); } elseif ( isset( $h2_matches[0][0] ) ) { // 指定された位置のH2がない場合は最初のH2の前に挿入 $insert_position = $h2_matches[0][0][1]; $content = substr_replace( $content, $toc_html, $insert_position, 0 ); } return $content; } /** * 目次用CSS出力 */ function aics_toc_styles() { if ( ! is_singular( array( 'post', 'page' ) ) ) { return; } $inline_style = " .aics-table-of-contents { background: var(--color-background-alt); border: 2px solid var(--color-border); border-radius: var(--border-radius); padding: var(--spacing-md); margin: var(--spacing-xl) 0; } .toc-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--spacing-sm); } .toc-title { font-weight: 700; font-size: 1.125rem; color: var(--color-primary); } .toc-toggle { background: none; border: none; cursor: pointer; padding: 0.25rem; transition: transform var(--transition-base); } .toc-toggle-icon { display: inline-block; font-size: 0.875rem; } .toc-content { max-height: 500px; overflow-y: auto; } .toc-list { list-style: none; counter-reset: toc-counter; } .toc-item { counter-increment: toc-counter; margin: 0.5rem 0; } .toc-item::before { content: counter(toc-counter) '. '; color: var(--color-text-light); font-weight: 600; } .toc-link { color: var(--color-text); text-decoration: none; transition: color var(--transition-base); } .toc-link:hover { color: var(--color-primary); } /* インデント */ .toc-level-2 { padding-left: 0; } .toc-level-3 { padding-left: 1rem; } .toc-level-4 { padding-left: 2rem; } /* 折りたたみ状態 */ .toc-collapsed .toc-content { display: none; } .toc-collapsed .toc-toggle-icon { transform: rotate(-90deg); } "; wp_add_inline_style( 'aics-style', $inline_style ); } add_action( 'wp_enqueue_scripts', 'aics_toc_styles' ); /** * 目次用JavaScript出力 */ function aics_toc_scripts() { if ( ! is_singular( array( 'post', 'page' ) ) ) { return; } $inline_script = " document.addEventListener('DOMContentLoaded', function() { const toc = document.querySelector('.aics-table-of-contents'); if (!toc) return; const toggleBtn = toc.querySelector('.toc-toggle'); if (!toggleBtn) return; // 目次の開閉 toggleBtn.addEventListener('click', function() { toc.classList.toggle('toc-collapsed'); }); // スムーズスクロール const tocLinks = toc.querySelectorAll('.toc-link'); tocLinks.forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); const targetId = this.getAttribute('href').substring(1); const targetElement = document.getElementById(targetId); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); // URLを更新(履歴に追加) history.pushState(null, null, '#' + targetId); } }); }); }); "; wp_add_inline_script( 'aics-main', $inline_script ); } add_action( 'wp_enqueue_scripts', 'aics_toc_scripts', 999 );