2026-02-12 10:31:53 +08:00
@ extends ( 'layouts.site' )
@ section ( 'page_class' , 'page-tools' )
2026-02-12 10:57:53 +08:00
@ section ( 'title' , 'AI工具集 - AIWeb' )
2026-02-12 15:37:49 +08:00
@ section ( 'meta_description' , 'AI工具集首页, 按分类分块浏览工具, 左侧菜单可定位到对应模块。' )
2026-02-12 10:31:53 +08:00
@ section ( 'canonical' , route ( 'tools.index' ))
@ section ( 'head' )
< style >
2026-02-12 15:37:49 +08:00
html { scroll - behavior : smooth ; }
2026-02-12 17:10:36 +08:00
. tool - home { display : grid ; grid - template - columns : 208 px minmax ( 0 , 1 fr ); gap : . 72 rem ; align - items : start ; }
. tool - side { position : sticky ; top : . 72 rem ; border : 1 px solid var ( -- line ); border - radius : 12 px ; background : var ( -- bg - surface ); box - shadow : var ( -- shadow - sm ); padding : . 6 rem . 48 rem ; max - height : calc ( 100 vh - 1.44 rem ); overflow : auto ; }
. tool - side - logo { display : flex ; align - items : center ; gap : . 38 rem ; padding : . 22 rem . 3 rem . 56 rem ; border - bottom : 1 px solid var ( -- line ); margin - bottom : . 36 rem ; font - family : " Outfit " , " Noto Sans SC " , sans - serif ; color : var ( -- text - main ); font - size : 1.12 rem ; font - weight : 800 ; }
2026-02-12 15:37:49 +08:00
. tool - side - logo - dot { width : 1.35 rem ; height : 1.35 rem ; border - radius : . 38 rem ; background : linear - gradient ( 135 deg , #a777ff, #6e77ff); color: #fff; font-size: .68rem; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
. tool - side - links { display : grid ; gap : . 16 rem ; }
. tool - side - link { display : grid ; grid - template - columns : 1 rem minmax ( 0 , 1 fr ) auto ; align - items : center ; gap : . 42 rem ; border : 1 px solid transparent ; border - radius : . 58 rem ; text - decoration : none ; color : #4e6189; padding: .38rem .42rem; font-size: .82rem; transition: .16s ease; }
2026-02-12 17:10:36 +08:00
. tool - side - link : hover , . tool - side - link . active , . tool - side - link . is - active { border - color : var ( -- line - strong ); background : var ( -- brand - soft ); color : #21345f; box-shadow: inset 3px 0 0 #5f72ff; }
2026-02-12 15:37:49 +08:00
. tool - side - link i { color : #687ba2; text-align: center; }
. tool - side - link small { color : #97a9c8; font-size: .7rem; }
. tool - main { min - width : 0 ; display : grid ; gap : . 62 rem ; }
2026-02-12 17:10:36 +08:00
. tool - hero { border : 1 px solid var ( -- line ); border - radius : 12 px ; background : linear - gradient ( 180 deg , #f5f8ff 0, #eef3fb 100%); box-shadow: var(--shadow-sm); padding: .95rem .95rem .82rem; text-align: center; }
. tool - chip { display : inline - flex ; border : 1 px solid var ( -- line - strong ); border - radius : 999 px ; background : var ( -- bg - surface ); color : #6f81a7; font-size: .68rem; font-weight: 700; padding: .14rem .5rem; }
. tool - title { margin : . 34 rem 0 . 18 rem ; font - family : " Outfit " , " Noto Sans SC " , sans - serif ; font - size : clamp ( 1.6 rem , 2.8 vw , 2.45 rem ); font - weight : 800 ; color : var ( -- text - main ); }
. tool - sub { margin : 0 ; color : var ( -- text - muted ); font - size : . 84 rem ; }
2026-02-12 15:37:49 +08:00
2026-02-12 17:10:36 +08:00
: root [ data - theme = " dark " ] . tool - hero { background : linear - gradient ( 180 deg , #1b2849 0, #16223f 100%); }
. tool - search { margin : . 62 rem auto . 38 rem ; width : min ( 860 px , 100 % ); display : grid ; grid - template - columns : minmax ( 0 , 1 fr ) auto ; gap : . 38 rem ; border : 1 px solid var ( -- line - strong ); border - radius : 999 px ; background : var ( -- bg - surface ); padding : . 3 rem ; }
. tool - search input { border : 0 ; box - shadow : none ; height : 2.16 rem ; padding : 0 . 82 rem ; background : transparent ; color : var ( -- text - main ); }
2026-02-12 15:37:49 +08:00
. tool - search button { border - radius : 999 px ; min - width : 90 px ; height : 2.16 rem ; font - size : . 82 rem ; }
. tool - kpis { display : grid ; grid - template - columns : repeat ( 4 , minmax ( 0 , 1 fr )); gap : . 5 rem ; margin - top : . 52 rem ; }
2026-02-12 17:10:36 +08:00
. tool - kpi { border : 1 px solid var ( -- line ); border - radius : . 7 rem ; background : var ( -- bg - surface ); padding : . 46 rem . 5 rem ; }
2026-02-12 15:37:49 +08:00
. tool - kpi . label { display : block ; color : #7b8fb2; font-size: .72rem; }
2026-02-12 17:10:36 +08:00
. tool - kpi . value { margin - top : . 12 rem ; font - family : " Outfit " , " Noto Sans SC " , sans - serif ; font - size : 1.08 rem ; font - weight : 800 ; color : var ( -- text - main ); }
2026-02-12 15:37:49 +08:00
2026-02-12 17:10:36 +08:00
. tool - channel { border : 1 px solid var ( -- line ); border - radius : 12 px ; background : var ( -- bg - surface ); box - shadow : var ( -- shadow - sm ); padding : . 52 rem ; display : grid ; grid - template - columns : 62 px repeat ( 5 , minmax ( 0 , 1 fr )); gap : . 46 rem ; }
. tool - channel - mini { border : 1 px solid var ( -- line ); border - radius : 10 px ; background : var ( -- bg - soft ); display : grid ; gap : . 34 rem ; padding : . 44 rem . 3 rem ; }
2026-02-12 15:37:49 +08:00
. tool - channel - mini a { text - decoration : none ; color : #5f7399; font-size: .72rem; text-align: center; }
2026-02-12 17:10:36 +08:00
. tool - channel - card { border : 1 px solid var ( -- line ); border - radius : 10 px ; min - height : 104 px ; display : flex ; align - items : flex - end ; justify - content : flex - start ; text - decoration : none ; font - family : " Outfit " , " Noto Sans SC " , sans - serif ; font - size : . 92 rem ; font - weight : 700 ; color : #fff; background-size: cover; background-position: center; position: relative; overflow: hidden; padding: .52rem; }
. tool - channel - card :: after { content : '' ; position : absolute ; inset : 0 ; background : linear - gradient ( 180 deg , rgba ( 20 , 32 , 56 , . 08 ), rgba ( 20 , 32 , 56 , . 68 )); }
2026-02-12 15:37:49 +08:00
. tool - channel - card span { position : relative ; z - index : 1 ; }
. tool - banner - row { display : grid ; grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr )); gap : . 52 rem ; }
2026-02-12 17:10:36 +08:00
. tool - banner { border : 1 px solid var ( -- line ); border - radius : 10 px ; background : linear - gradient ( 90 deg , #e8f3ff, #e4f7ef); min-height: 72px; display: flex; align-items: center; justify-content: space-between; gap: .52rem; padding: .58rem .72rem; text-decoration: none; }
2026-02-12 15:37:49 +08:00
. tool - banner . alt { background : linear - gradient ( 90 deg , #e6f6ff, #dff8dd); }
2026-02-12 17:10:36 +08:00
. tool - banner b { color : var ( -- text - main ); font - size : 1.06 rem ; font - family : " Outfit " , " Noto Sans SC " , sans - serif ; }
. tool - banner small { border : 1 px solid var ( -- line - strong ); border - radius : 999 px ; background : var ( -- bg - surface ); color : #5f749a; font-size: .72rem; padding: .14rem .48rem; }
2026-02-12 15:37:49 +08:00
2026-02-12 17:10:36 +08:00
. tool - section { border : 1 px solid var ( -- line ); border - radius : 12 px ; background : var ( -- bg - surface ); box - shadow : var ( -- shadow - sm ); padding : . 62 rem ; scroll - margin - top : 1 rem ; }
2026-02-12 15:37:49 +08:00
. tool - section - head { display : flex ; align - items : center ; justify - content : space - between ; margin - bottom : . 48 rem ; }
2026-02-12 17:10:36 +08:00
. tool - section - head h2 { margin : 0 ; color : var ( -- text - main ); font - size : 1 rem ; font - family : " Outfit " , " Noto Sans SC " , sans - serif ; font - weight : 700 ; }
. tool - section - head a { color : var ( -- brand - strong ); text - decoration : none ; font - size : . 76 rem ; font - weight : 600 ; }
. tool - grid { display : grid ; grid - template - columns : repeat ( 3 , minmax ( 0 , 1 fr )); gap : . 52 rem ; }
. tool - card { border : 1 px solid var ( -- line ); border - radius : 10 px ; background : var ( -- bg - surface ); box - shadow : var ( -- shadow - sm ); padding : . 56 rem . 62 rem ; text - decoration : none ; transition : . 16 s ease ; }
. tool - card : hover { transform : translateY ( - 2 px ); border - color : var ( -- line - strong ); box - shadow : 0 12 px 22 px rgba ( 31 , 54 , 112 , . 14 ); }
. tool - card - name { color : var ( -- text - main ); font - size : . 92 rem ; line - height : 1.26 ; font - weight : 700 ; }
. tool - card - meta { margin - top : . 2 rem ; color : var ( -- text - muted ); font - size : . 76 rem ; }
. tool - card - tag { margin - top : . 42 rem ; display : inline - flex ; border : 1 px solid var ( -- line - strong ); border - radius : 999 px ; padding : . 13 rem . 48 rem ; color : var ( -- brand - strong ); background : var ( -- brand - soft ); font - size : . 7 rem ; }
2026-02-12 15:37:49 +08:00
@ media ( max - width : 1199 px ) {
. tool - home { grid - template - columns : 1 fr ; }
. tool - side { position : static ; max - height : none ; }
2026-02-12 10:57:53 +08:00
}
2026-02-12 15:37:49 +08:00
@ media ( max - width : 767 px ) {
. tool - kpis { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr )); }
2026-02-12 17:10:36 +08:00
. tool - grid { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr )); }
2026-02-12 15:37:49 +08:00
. tool - search { grid - template - columns : 1 fr ; border - radius : 12 px ; }
. tool - search button { width : 100 % ; border - radius : 10 px ; }
2026-02-12 17:10:36 +08:00
. tool - channel { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr )); }
2026-02-12 15:37:49 +08:00
. tool - banner - row { grid - template - columns : 1 fr ; }
2026-02-12 10:31:53 +08:00
}
</ style >
@ endsection
@ section ( 'content' )
@ php
$icons = [ 'bi-stars' , 'bi-pencil' , 'bi-image' , 'bi-camera-video' , 'bi-briefcase' , 'bi-cpu' , 'bi-chat-dots' , 'bi-code-slash' , 'bi-kanban' ];
2026-02-12 15:37:49 +08:00
$channelModule = $modules [ 'channel_cards' ] ? ? null ;
$bannerModule = $modules [ 'promo_banners' ] ? ? null ;
$hotModule = $modules [ 'hot_tools' ] ? ? null ;
$latestModule = $modules [ 'latest_tools' ] ? ? null ;
$categoryModule = $modules [ 'category_sections' ] ? ? null ;
$moduleOrder = collect ( $modules ) -> sortBy ( 'sort_order' ) -> pluck ( 'module_key' ) -> values ();
2026-02-12 17:10:36 +08:00
$channelItems = collect ( data_get ( $channelModule , 'items' , [])) -> take (( int ) data_get ( $channelModule , 'limit' , 5 ));
$bannerItems = collect ( data_get ( $bannerModule , 'items' , [])) -> take (( int ) data_get ( $bannerModule , 'limit' , 2 ));
2026-02-12 10:31:53 +08:00
@ endphp
2026-02-12 10:57:53 +08:00
< div class = " tool-home " >
< aside class = " tool-side " aria - label = " 工具分类侧边栏 " >
2026-02-12 15:37:49 +08:00
< div class = " tool-side-logo " >< span class = " tool-side-logo-dot " > AI </ span > {{ data_get ( $categoryModule , 'extra.side_title' , 'AI工具集' ) }} </ div >
2026-02-12 10:57:53 +08:00
< nav class = " tool-side-links " >
2026-02-12 17:10:36 +08:00
@ if ( data_get ( $channelModule , 'enabled' , true ) === true )
< a class = " tool-side-link " href = " #section-channel " >
< i class = " bi bi-grid " ></ i >
< span > {{ data_get ( $channelModule , 'title' , '频道卡片' ) }} </ span >
< small > {{ $channelItems -> count () }} </ small >
</ a >
@ endif
@ if ( data_get ( $bannerModule , 'enabled' , true ) === true )
< a class = " tool-side-link " href = " #section-banner " >
< i class = " bi bi-megaphone " ></ i >
< span > {{ data_get ( $bannerModule , 'title' , '横幅推荐' ) }} </ span >
< small > {{ $bannerItems -> count () }} </ small >
</ a >
@ endif
2026-02-12 15:37:49 +08:00
@ if ( data_get ( $hotModule , 'enabled' , true ) === true )
2026-02-12 13:06:12 +08:00
< a class = " tool-side-link " href = " #section-hot " >
< i class = " bi bi-fire " ></ i >
2026-02-12 15:37:49 +08:00
< span > {{ data_get ( $hotModule , 'title' , '热门工具' ) }} </ span >
2026-02-12 13:06:12 +08:00
< small > {{ $hotTools -> count () }} </ small >
2026-02-12 10:31:53 +08:00
</ a >
2026-02-12 13:06:12 +08:00
@ endif
2026-02-12 15:37:49 +08:00
@ if ( data_get ( $latestModule , 'enabled' , true ) === true )
2026-02-12 13:06:12 +08:00
< a class = " tool-side-link " href = " #section-latest " >
< i class = " bi bi-clock-history " ></ i >
2026-02-12 15:37:49 +08:00
< span > {{ data_get ( $latestModule , 'title' , '最新收录' ) }} </ span >
2026-02-12 13:06:12 +08:00
< small > {{ $latestTools -> count () }} </ small >
</ a >
@ endif
2026-02-12 15:37:49 +08:00
@ if ( data_get ( $categoryModule , 'enabled' , true ) === true )
2026-02-12 13:06:12 +08:00
@ foreach ( $categorySections as $index => $section )
< a class = " tool-side-link " href = " #section- { { $section['slug'] }} " >
< i class = " bi { { $icons [ $index % count( $icons )] }} " ></ i >
< span > {{ $section [ 'name' ] }} </ span >
< small > {{ $section [ 'count' ] }} </ small >
</ a >
@ endforeach
@ endif
2026-02-12 10:31:53 +08:00
</ nav >
</ aside >
2026-02-12 10:57:53 +08:00
< section class = " tool-main " >
< section class = " tool-hero " >
< span class = " tool-chip " > AI - BOT . CN </ span >
2026-02-12 15:37:49 +08:00
< h1 class = " tool-title " > {{ data_get ( $hotModule , 'extra.side_title' , 'AI工具集' ) }} </ h1 >
< p class = " tool-sub " > {{ data_get ( $hotModule , 'subtitle' , '左侧菜单点击后可直接定位到对应工具区块。' ) }} </ p >
2026-02-12 10:31:53 +08:00
2026-02-12 10:57:53 +08:00
< form class = " tool-search " method = " get " action = " { { route('tools.list') }} " role = " search " aria - label = " 站内 AI 工具搜索 " >
2026-02-12 10:31:53 +08:00
< input type = " hidden " name = " tab " value = " { { $activeTab }} " >
2026-02-12 15:37:49 +08:00
< input type = " search " name = " q " value = " { { $filters['q'] ?? '' }} " placeholder = " 站内 AI 工具搜索,如:写作、图像、自动化 " autocomplete = " off " spellcheck = " false " >
2026-02-12 10:31:53 +08:00
< button class = " btn btn-primary " type = " submit " >< i class = " bi bi-search " ></ i > 搜索 </ button >
</ form >
2026-02-12 10:57:53 +08:00
< nav class = " channel-tabs justify-content-center " aria - label = " 筛选模式 " >
@ foreach ( $tabOptions as $tab )
< a class = " channel-tab @if( $activeTab === $tab['key'] ) active @endif " href = " { { route('tools.index', array_filter(array_merge( $filters , ['tab' => $tab['key'] ]))) }} " > {{ $tab [ 'label' ] }} </ a >
@ endforeach
2026-02-12 10:31:53 +08:00
</ nav >
2026-02-12 13:06:12 +08:00
< x - portal . stat - grid
grid - class = " tool-kpis "
item - class = " tool-kpi "
: stats = " [
[ 'label' => '收录工具' , 'value' => $toolStats [ 'total' ] ? ? 0 ],
[ 'label' => '支持 API' , 'value' => $toolStats [ 'api' ] ? ? 0 ],
[ 'label' => '免费可用' , 'value' => $toolStats [ 'free' ] ? ? 0 ],
[ 'label' => '7天更新' , 'value' => $toolStats [ 'updated_7d' ] ? ? 0 ],
] "
/>
2026-02-12 10:31:53 +08:00
</ section >
2026-02-12 15:37:49 +08:00
@ foreach ( $moduleOrder as $moduleKey )
@ if ( $moduleKey === 'channel_cards' && data_get ( $channelModule , 'enabled' , true ) === true )
@ php
$miniItems = $channelItems -> take ( 4 );
@ endphp
2026-02-12 17:10:36 +08:00
< section id = " section-channel " class = " tool-section " aria - label = " 频道入口 " >
< header class = " tool-section-head " >
< h2 >< i class = " bi bi-grid text-primary " ></ i > {{ data_get ( $channelModule , 'title' , '频道卡片' ) }} </ h2 >
< a href = " { { route('tools.list') }} " > 查看更多 </ a >
</ header >
< div class = " tool-channel " >
2026-02-12 15:37:49 +08:00
< aside class = " tool-channel-mini " >
@ foreach ( $miniItems as $item )
< a href = " { { $item['url'] ?: 'javascript:void(0)' }} " > {{ $item [ 'title' ] ? : '频道' }} </ a >
@ endforeach
</ aside >
2026-02-12 10:31:53 +08:00
2026-02-12 15:37:49 +08:00
@ foreach ( $channelItems as $item )
< a class = " tool-channel-card " href = " { { $item['url'] ?: 'javascript:void(0)' }} " @ if ( ! empty ( $item [ 'image_path' ])) style = " background-image:url(' { { $item['image_path'] }}'); " @ endif >
< span > {{ $item [ 'title' ] ? : '频道入口' }} </ span >
</ a >
@ endforeach
2026-02-12 17:10:36 +08:00
</ div >
2026-02-12 15:37:49 +08:00
</ section >
@ endif
2026-02-12 10:31:53 +08:00
2026-02-12 15:37:49 +08:00
@ if ( $moduleKey === 'promo_banners' && data_get ( $bannerModule , 'enabled' , true ) === true )
2026-02-12 17:10:36 +08:00
< section id = " section-banner " class = " tool-section " aria - label = " 横幅推荐 " >
< header class = " tool-section-head " >
< h2 >< i class = " bi bi-megaphone text-primary " ></ i > {{ data_get ( $bannerModule , 'title' , '横幅推荐' ) }} </ h2 >
< a href = " { { route('tools.list') }} " > 查看更多 </ a >
</ header >
< div class = " tool-banner-row " >
2026-02-12 15:37:49 +08:00
@ forelse ( $bannerItems as $banner )
< a class = " tool-banner @if( $loop->index % 2 === 1) alt @endif " href = " { { $banner['url'] ?: 'javascript:void(0)' }} " @ if ( ! empty ( $banner [ 'image_path' ])) style = " background-image:url(' { { $banner['image_path'] }}'); background-size:cover; " @ endif >
< b > {{ $banner [ 'title' ] ? : '精选推荐' }} </ b >
< small > {{ $banner [ 'subtitle' ] ? : '查看详情' }} </ small >
</ a >
@ empty
< article class = " tool-banner " >< b > 暂无横幅推荐 </ b >< small > 请在后台配置 </ small ></ article >
@ endforelse
2026-02-12 17:10:36 +08:00
</ div >
2026-02-12 15:37:49 +08:00
</ section >
@ endif
2026-02-12 10:57:53 +08:00
2026-02-12 15:37:49 +08:00
@ if ( $moduleKey === 'hot_tools' && data_get ( $hotModule , 'enabled' , true ) === true )
< section id = " section-hot " class = " tool-section " aria - label = " 热门工具 " >
< header class = " tool-section-head " >
< h2 >< i class = " bi bi-fire text-danger " ></ i > {{ data_get ( $hotModule , 'title' , '热门工具' ) }} </ h2 >
@ php ( $hotMore = data_get ( $hotModule , 'more_url' ) ? : route ( 'tools.list' , [ 'tab' => 'recommended' ]))
< a href = " { { $hotMore }} " > 查看更多 </ a >
</ header >
< x - portal . tool - grid : tools = " $hotTools " />
</ section >
@ endif
2026-02-12 10:57:53 +08:00
2026-02-12 15:37:49 +08:00
@ if ( $moduleKey === 'latest_tools' && data_get ( $latestModule , 'enabled' , true ) === true )
< section id = " section-latest " class = " tool-section " aria - label = " 最新收录 " >
< header class = " tool-section-head " >
< h2 >< i class = " bi bi-clock-history text-primary " ></ i > {{ data_get ( $latestModule , 'title' , '最新收录' ) }} </ h2 >
@ php ( $latestMore = data_get ( $latestModule , 'more_url' ) ? : route ( 'tools.list' , [ 'tab' => 'latest' ]))
< a href = " { { $latestMore }} " > 查看更多 </ a >
</ header >
< x - portal . tool - grid : tools = " $latestTools " />
</ section >
@ endif
2026-02-12 10:57:53 +08:00
2026-02-12 15:37:49 +08:00
@ if ( $moduleKey === 'category_sections' && data_get ( $categoryModule , 'enabled' , true ) === true )
@ foreach ( $categorySections as $section )
< section id = " section- { { $section['slug'] }} " class = " tool-section " aria - label = " { { $section['name'] }}工具 " >
< header class = " tool-section-head " >
< h2 >< i class = " bi bi-grid-3x3-gap text-primary " ></ i > {{ $section [ 'name' ] }} </ h2 >
< a href = " { { route('tools.list', ['category' => $section['slug'] ]) }} " > 查看更多 </ a >
</ header >
@ if ( $section [ 'tools' ] -> isNotEmpty ())
< x - portal . tool - grid : tools = " $section['tools'] " />
@ else
< p class = " text-muted-soft mb-0 " > 该分类暂未收录工具 </ p >
@ endif
</ section >
@ endforeach
@ endif
2026-02-12 10:57:53 +08:00
@ endforeach
2026-02-12 10:31:53 +08:00
</ section >
</ div >
@ endsection
2026-02-12 10:57:53 +08:00
2026-02-12 13:06:12 +08:00
@ section ( 'scripts' )
< script >
(() => {
const links = Array . from ( document . querySelectorAll ( '.tool-side-link[href^="#section-"]' ));
2026-02-12 17:10:36 +08:00
const sections = Array . from ( document . querySelectorAll ( '[id^="section-"]' ));
2026-02-12 13:06:12 +08:00
if ( ! links . length || ! sections . length || ! ( 'IntersectionObserver' in window )) {
return ;
}
const setActive = ( activeId ) => {
links . forEach (( link ) => {
const id = link . getAttribute ( 'href' ) ? . replace ( '#' , '' ) || '' ;
link . classList . toggle ( 'is-active' , id === activeId );
});
};
const syncHash = ( id ) => {
const hash = `#${id}` ;
if ( window . location . hash !== hash ) {
history . replaceState ( null , '' , hash );
}
};
links . forEach (( link ) => {
link . addEventListener ( 'click' , ( event ) => {
const id = link . getAttribute ( 'href' ) ? . replace ( '#' , '' );
if ( ! id ) {
return ;
}
const target = document . getElementById ( id );
if ( ! target ) {
return ;
}
event . preventDefault ();
setActive ( id );
syncHash ( id );
target . scrollIntoView ({ behavior : 'smooth' , block : 'start' });
});
});
const observer = new IntersectionObserver (( entries ) => {
const visible = entries
. filter (( entry ) => entry . isIntersecting )
. sort (( a , b ) => b . intersectionRatio - a . intersectionRatio );
if ( ! visible . length ) {
return ;
}
const top = visible [ 0 ] . target ;
if ( top instanceof HTMLElement ) {
setActive ( top . id );
syncHash ( top . id );
}
}, {
rootMargin : '-18% 0px -64% 0px' ,
threshold : [ 0.2 , 0.45 , 0.7 ],
});
sections . forEach (( section ) => observer . observe ( section ));
if ( window . location . hash ) {
const hashId = window . location . hash . replace ( '#' , '' );
const target = document . getElementById ( hashId );
if ( target ) {
setActive ( hashId );
window . requestAnimationFrame (() => {
target . scrollIntoView ({ behavior : 'auto' , block : 'start' });
});
} else {
setActive ( sections [ 0 ] . id );
syncHash ( sections [ 0 ] . id );
}
} else {
setActive ( sections [ 0 ] . id );
syncHash ( sections [ 0 ] . id );
}
})();
</ script >
@ endsection