Browse Source

前端重构

visuddhinanda 4 days ago
parent
commit
d92fec7881
42 changed files with 3473 additions and 374 deletions
  1. 1 1
      api-v12/app/Http/Controllers/Library/SearchController.php
  2. 5 4
      api-v12/app/Http/Controllers/Library/WikiController.php
  3. 384 0
      api-v12/documents/wikipali-frontend-spec.md
  4. 0 11
      api-v12/resources/css/app.css
  5. 16 0
      api-v12/resources/css/base/_reset.css
  6. 13 0
      api-v12/resources/css/base/_typography.css
  7. 39 0
      api-v12/resources/css/base/_variables.css
  8. 4 0
      api-v12/resources/css/components/_badge.css
  9. 23 0
      api-v12/resources/css/components/_card-book.css
  10. 5 0
      api-v12/resources/css/components/_card.css
  11. 64 0
      api-v12/resources/css/components/_pagination.css
  12. 13 0
      api-v12/resources/css/components/_search-input.css
  13. 9 84
      api-v12/resources/css/components/_search-results.css
  14. 92 0
      api-v12/resources/css/layout/_drawer.css
  15. 4 0
      api-v12/resources/css/layout/_footer.css
  16. 46 0
      api-v12/resources/css/layout/_grid.css
  17. 76 0
      api-v12/resources/css/layout/_hero.css
  18. 119 0
      api-v12/resources/css/layout/_navbar.css
  19. 4 0
      api-v12/resources/css/layout/_toolbar.css
  20. 33 0
      api-v12/resources/css/library.css
  21. 997 0
      api-v12/resources/css/modules/_wiki.css
  22. 163 0
      api-v12/resources/css/modules/_wiki_home_append.css
  23. 5 3
      api-v12/resources/js/app.js
  24. 30 0
      api-v12/resources/js/modules/navbar.js
  25. 233 0
      api-v12/resources/js/modules/term-tooltip.js
  26. 24 0
      api-v12/resources/views/components/library/footer.blade.php
  27. 62 0
      api-v12/resources/views/components/library/navbar.blade.php
  28. 35 0
      api-v12/resources/views/components/ui/empty-state.blade.php
  29. 42 0
      api-v12/resources/views/components/ui/search-input.blade.php
  30. 26 0
      api-v12/resources/views/layouts/base.blade.php
  31. 37 0
      api-v12/resources/views/layouts/library.blade.php
  32. 423 251
      api-v12/resources/views/library/anthology/index.blade.php
  33. 17 16
      api-v12/resources/views/library/anthology/pagination.blade.php
  34. 1 0
      api-v12/resources/views/library/anthology/show.blade.php
  35. 1 0
      api-v12/resources/views/library/book/read.blade.php
  36. 112 0
      api-v12/resources/views/library/search.blade.php
  37. 80 0
      api-v12/resources/views/library/wiki/home.blade.php
  38. 68 0
      api-v12/resources/views/library/wiki/index.blade.php
  39. 78 0
      api-v12/resources/views/library/wiki/layouts/app.blade.php
  40. 80 0
      api-v12/resources/views/library/wiki/show.blade.php
  41. 1 1
      api-v12/routes/web.php
  42. 8 3
      api-v12/vite.config.js

+ 1 - 1
api-v12/app/Http/Controllers/Library/SearchController.php

@@ -54,7 +54,7 @@ class SearchController extends Controller
             'last_page'    => max(1, (int) ceil($dto->hits->total / $perPage)),
             'last_page'    => max(1, (int) ceil($dto->hits->total / $perPage)),
         ];
         ];
 
 
-        return view('wiki.search', [
+        return view('library.search', [
             'lang'           => $lang,
             'lang'           => $lang,
             'query'          => $query,
             'query'          => $query,
             'results'        => $results,
             'results'        => $results,

+ 5 - 4
api-v12/app/Http/Controllers/WikiController.php → api-v12/app/Http/Controllers/Library/WikiController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Library;
 
 
+use App\Http\Controllers\Controller;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Helpers\WikiContentParser;
 use App\Helpers\WikiContentParser;
 use App\Services\TermService;
 use App\Services\TermService;
@@ -95,7 +96,7 @@ HTML,
         ];
         ];
 
 
 
 
-        return view('wiki.index', [
+        return view('library.wiki.index', [
             'today'         => $today,
             'today'         => $today,
             'featured'      => $this->featured($terms),
             'featured'      => $this->featured($terms),
             'stats'         => $this->mockStats(),
             'stats'         => $this->mockStats(),
@@ -134,7 +135,7 @@ HTML,
         ];
         ];
         $parsed  = WikiContentParser::parse($entry['content']);
         $parsed  = WikiContentParser::parse($entry['content']);
 
 
-        return view('wiki.show', [
+        return view('library.wiki.show', [
             'entry' => array_merge($entry, [
             'entry' => array_merge($entry, [
                 'content' => $parsed['content'],
                 'content' => $parsed['content'],
                 'toc'     => $parsed['toc'],
                 'toc'     => $parsed['toc'],
@@ -195,7 +196,7 @@ HTML,
             'meaning'   => '佛陀的偈颂集,佛教最重要的经典之一',
             'meaning'   => '佛陀的偈颂集,佛教最重要的经典之一',
         ];
         ];
 
 
-        return view('wiki.home', [
+        return view('library.wiki.home', [
             'languages'     => $languages,
             'languages'     => $languages,
             'currentLang'   => $lang,
             'currentLang'   => $lang,
             'stats'         => $stats,
             'stats'         => $stats,

+ 384 - 0
api-v12/documents/wikipali-frontend-spec.md

@@ -0,0 +1,384 @@
+# WikiPali 前端开发规范
+
+> 版本:1.0 · 适用范围:`library/*` · `blog/*` 全站前端
+
+---
+
+## 1. 技术栈
+
+| 层级 | 技术 |
+|---|---|
+| 后端框架 | Laravel 12 |
+| 模板引擎 | Blade |
+| CSS 框架 | Tabler CSS(基于 Bootstrap 5) |
+| 构建工具 | Vite |
+| 交互 | Vanilla JS + Tabler Offcanvas/Dropdown |
+| 搜索引擎 | OpenSearch |
+
+无 Livewire、无 Alpine.js、无额外前端框架。JS 保持轻量,仅在必要时引入模块。
+
+---
+
+## 2. 目录结构
+
+```
+resources/
+├── css/
+│   ├── library.css              # library/* 入口(纯 @import)
+│   ├── blog.css                 # blog/* 入口(纯 @import)
+│   ├── reader.css               # 全站阅读页入口(纯 @import)
+│   ├── app.css                  # 过渡期保留,稳定后删除
+│   │
+│   ├── base/
+│   │   ├── _variables.css       # Tabler 变量覆盖 + 自定义 token
+│   │   ├── _reset.css           # normalize / reset
+│   │   └── _typography.css      # 字体、行高、标题
+│   │
+│   ├── layout/
+│   │   ├── _grid.css            # 断点定义 + 1/2/3 栏容器(全站唯一)
+│   │   ├── _navbar.css          # 顶部导航(desktop 展开 / mobile 汉堡)
+│   │   ├── _drawer.css          # 左右 Drawer(基于 Tabler Offcanvas)
+│   │   ├── _hero.css            # Hero 区域
+│   │   ├── _footer.css
+│   │   └── _toolbar.css         # 页内工具条
+│   │
+│   ├── components/
+│   │   ├── _search-input.css    # 搜索框 + 提示下拉
+│   │   ├── _search-results.css  # 搜索结果页布局
+│   │   ├── _card.css            # 通用卡片(title + list)
+│   │   ├── _card-book.css       # 书籍卡片(封面 + 标题)
+│   │   ├── _badge.css
+│   │   └── _pagination.css
+│   │
+│   └── modules/                 # 各栏目差异样式,仅覆盖变量或追加规则
+│       ├── _wiki.css            # wiki 专属(词条详情、质量徽章等)
+│       ├── _tipitaka.css
+│       ├── _anthology.css
+│       ├── _blog.css
+│       └── _reader.css          # 阅读正文区(字号、行高、夜间模式)
+│
+├── js/
+│   ├── app.js                   # 全局入口
+│   ├── bootstrap.js
+│   └── modules/
+│       ├── term-tooltip.js      # 术语提示(原 term-tooltip.js)
+│       ├── toc.js               # TOC 折叠 + 滚动高亮
+│       ├── theme.js             # 夜间模式切换
+│       └── search-suggest.js    # 搜索提示下拉
+│
+└── views/
+    ├── layouts/                 # 布局文件(全站仅 3 个)
+    │   ├── base.blade.php       # HTML 骨架
+    │   ├── library.blade.php    # library/* 含 navbar + footer
+    │   ├── blog.blade.php       # blog/* 独立三栏,无 library 导航
+    │   └── reader.blade.php     # 全站阅读页,沉浸式,无导航
+    │
+    ├── components/
+    │   ├── ui/                  # 纯 UI 组件,全站通用
+    │   │   ├── drawer.blade.php
+    │   │   ├── search-input.blade.php
+    │   │   ├── card.blade.php
+    │   │   ├── card-book.blade.php
+    │   │   ├── badge.blade.php
+    │   │   └── pagination.blade.php
+    │   │
+    │   └── library/             # library 布局专用
+    │       ├── navbar.blade.php
+    │       ├── nav-links.blade.php
+    │       ├── hero.blade.php
+    │       ├── toolbar.blade.php
+    │       ├── footer.blade.php
+    │       ├── toc.blade.php
+    │       ├── layout-1col.blade.php
+    │       ├── layout-2col.blade.php
+    │       └── layout-3col.blade.php
+    │
+    ├── library/
+    │   ├── index.blade.php
+    │   ├── search.blade.php          # 统一搜索结果页
+    │   ├── tipitaka/
+    │   │   ├── index.blade.php
+    │   │   ├── category.blade.php
+    │   │   ├── show.blade.php
+    │   │   └── read.blade.php
+    │   ├── anthology/
+    │   │   ├── index.blade.php
+    │   │   ├── show.blade.php
+    │   │   └── read.blade.php
+    │   └── wiki/
+    │       ├── home.blade.php
+    │       ├── index.blade.php
+    │       └── show.blade.php
+    │
+    └── blog/
+        ├── index.blade.php
+        ├── category.blade.php
+        └── show.blade.php
+```
+
+---
+
+## 3. 路由与页面对应
+
+```
+/library                           → library/index.blade.php
+/library/search?q=&type=          → library/search.blade.php
+/library/tipitaka                  → library/tipitaka/index.blade.php
+/library/tipitaka/category/{id}    → library/tipitaka/category.blade.php
+/library/tipitaka/{id}             → library/tipitaka/show.blade.php
+/library/tipitaka/{id}/read        → library/tipitaka/read.blade.php
+/library/anthology                 → library/anthology/index.blade.php
+/library/anthology/{id}            → library/anthology/show.blade.php
+/library/anthology/{id}/read/{art} → library/anthology/read.blade.php
+/library/wiki                      → library/wiki/home.blade.php
+/library/wiki/{lang}               → library/wiki/index.blade.php
+/library/wiki/{lang}/{word}        → library/wiki/show.blade.php
+/blog/{user}                       → blog/index.blade.php
+/blog/{user}/{post}                → blog/show.blade.php
+/blog/{user}/category/...          → blog/category.blade.php
+```
+
+---
+
+## 4. 栏目划分
+
+| 栏目 | 路由前缀 | 布局 | 说明 |
+|---|---|---|---|
+| Library 门户 | `/library` | `layouts/library` | 入口页,含 Hero |
+| Tipitaka(巴利三藏) | `/library/tipitaka` | `layouts/library` | 含阅读页 |
+| Anthology(文集) | `/library/anthology` | `layouts/library` | 含阅读页 |
+| Wiki(词典) | `/library/wiki` | `layouts/library` | 词条详情为阅读页 |
+| Search(搜索) | `/library/search` | `layouts/library` | 统一结果页,`?type=` 区分栏目 |
+| Blog(博客) | `/blog` | `layouts/blog` | 独立三栏,与 library 完全隔离 |
+
+**阅读页统一使用 `layouts/reader`**,包括:
+
+- `library/tipitaka/read.blade.php`
+- `library/anthology/read.blade.php`
+- `library/wiki/show.blade.php`(词条详情本质为阅读)
+- `blog/show.blade.php`(博文详情)
+
+---
+
+## 5. Blade 调用层级
+
+```
+layouts/base.blade.php
+│   HTML 骨架,@vite,@stack('styles'),@stack('scripts')
+│   不含任何导航或布局
+│
+├── layouts/library.blade.php
+│   @extends('layouts.base')
+│   包含:navbar + hero(可选) + toolbar(可选) + @yield('content') + footer
+│   页面通过 @section('content') 填充正文
+│   栏目模块 CSS 通过 @push('styles') 注入
+│
+├── layouts/blog.blade.php
+│   @extends('layouts.base')
+│   独立三栏:左侧博主信息 + 中间内容 + 右侧过滤工具
+│   无 library navbar / footer
+│
+└── layouts/reader.blade.php
+    @extends('layouts.base')
+    沉浸式,无 navbar,无 footer
+    包含:TOC(desktop 常驻,mobile 左侧 Drawer)+ 正文区
+```
+
+**页面文件示例:**
+
+```blade
+{{-- library/wiki/show.blade.php --}}
+@extends('layouts.reader')
+
+@push('styles')
+    @vite('resources/css/modules/_wiki.css')  {{-- wiki 专属样式 --}}
+@endpush
+
+@section('content')
+    ...
+@endsection
+```
+
+---
+
+## 6. CSS 架构与复用
+
+### 基准层
+
+wiki 栏目的现有 CSS 作为全站基准,其他栏目默认继承,需要时通过 `modules/_xxx.css` 覆盖。
+
+### 入口文件职责
+
+入口文件(`library.css` / `blog.css` / `reader.css`)**只做 `@import`,不写任何样式规则**。
+
+```css
+/* library.css */
+@import './base/_variables.css';
+@import './base/_reset.css';
+@import './base/_typography.css';
+@import './layout/_grid.css';
+@import './layout/_navbar.css';
+@import './layout/_drawer.css';
+@import './layout/_hero.css';
+@import './layout/_footer.css';
+@import './layout/_toolbar.css';
+@import './components/_search-input.css';
+@import './components/_card.css';
+@import './components/_card-book.css';
+@import './components/_badge.css';
+@import './components/_pagination.css';
+/* modules 不在此引入,由各页面按需 @push */
+```
+
+### 变量覆盖机制
+
+`_variables.css` 覆盖 Tabler 的 CSS custom properties,各栏目 module 文件**只重定义变量,不重写规则**:
+
+```css
+/* modules/_wiki.css */
+:root {
+  --color-primary: #5c6bc0;
+}
+/* 仅追加 wiki 专属规则 */
+```
+
+### Vite 入口配置
+
+```js
+// vite.config.js
+laravel({
+    input: [
+        'resources/css/library.css',
+        'resources/css/blog.css',
+        'resources/css/reader.css',
+        'resources/js/app.js',
+    ],
+    refresh: true,
+})
+```
+
+---
+
+## 7. 页面布局(从上到下)
+
+### library/* 页面
+
+```
+┌────────────────────────────────────┐
+│  Navbar                            │  所有 library 页面
+│  左:面包屑  右:导航按钮          │
+├────────────────────────────────────┤
+│  Hero(可选)                      │  首页、栏目首页
+├────────────────────────────────────┤
+│  Toolbar(可选)                   │  列表页、详情页
+├──────────┬────────────┬────────────┤
+│  左边栏  │  主内容    │  右边栏    │  3 栏(wiki 词条等)
+│(可选)  │            │(可选)    │  2 栏(tipitaka 等)
+│          │            │            │  1 栏(首页、搜索)
+└──────────┴────────────┴────────────┘
+│  Footer                            │
+└────────────────────────────────────┘
+```
+
+### blog/* 页面
+
+```
+┌──────────┬────────────┬────────────┐
+│  左边栏  │  文章列表  │  右边栏    │
+│          │            │            │
+│  头像    │  卡片列表  │  搜索框    │
+│  徽章    │  分页      │  标签云    │
+│  栏目导航│            │  过滤器    │
+└──────────┴────────────┴────────────┘
+```
+
+### 阅读页(reader)
+
+```
+┌──────────┬────────────────────────┐
+│  TOC     │  正文                  │
+│  常驻    │  无 navbar             │
+│          │  无 footer             │
+└──────────┴────────────────────────┘
+```
+
+---
+
+## 8. 响应式规范
+
+断点统一定义在 `layout/_grid.css`,全站唯一,不在各栏目重复定义。
+
+| 区域 | Desktop (>1024px) | Tablet (768–1024px) | Mobile (<768px) |
+|---|---|---|---|
+| 顶部导航 | 完整展开 | 完整展开 | 汉堡按钮 → 右侧 Drawer |
+| 左边栏 | 常驻 | 常驻 | 左侧 Drawer,默认收起 |
+| 右边栏 | 常驻 | 隐藏 | 隐藏 |
+| 阅读页 TOC | 左侧常驻 | 左侧 Drawer | 左侧 Drawer + 悬浮触发按钮 |
+| Blog 左边栏 | 常驻 | 左侧 Drawer | 左侧 Drawer |
+| Blog 右边栏 | 常驻 | 隐藏 | 隐藏 |
+
+### Drawer 组件
+
+基于 Tabler Offcanvas,左右方向通过 `side` prop 控制:
+
+```blade
+{{-- 导航 Drawer(右侧) --}}
+<x-ui.drawer id="nav-drawer" side="end" title="导航">
+    <x-library.nav-links />
+</x-ui.drawer>
+
+{{-- TOC Drawer(左侧) --}}
+<x-ui.drawer id="toc-drawer" side="start" title="目录">
+    <x-library.toc :items="$toc" />
+</x-ui.drawer>
+```
+
+---
+
+## 9. 可复用组件清单
+
+| 组件 | 路径 | Props | 用途 |
+|---|---|---|---|
+| `<x-ui.drawer>` | `components/ui/drawer` | `id` `side=start\|end` `title` | 左/右抽屉 |
+| `<x-ui.search-input>` | `components/ui/search-input` | `name` `action` `placeholder` `suggest-url` | 搜索框 + 提示 |
+| `<x-ui.card>` | `components/ui/card` | `title` `items` `href` | 通用卡片 |
+| `<x-ui.card-book>` | `components/ui/card-book` | `title` `cover` `href` `layout=vertical\|horizontal` | 书籍卡片 |
+| `<x-ui.badge>` | `components/ui/badge` | `type` `label` | 徽章 |
+| `<x-ui.pagination>` | `components/ui/pagination` | `paginator` | 分页 |
+| `<x-library.navbar>` | `components/library/navbar` | — | 含汉堡触发 |
+| `<x-library.toc>` | `components/library/toc` | `items` | TOC,自适应 Drawer |
+| `<x-library.hero>` | `components/library/hero` | `title` `subtitle` | Hero 区域 |
+| `<x-library.toolbar>` | `components/library/toolbar` | slot | 页内工具条 |
+| `<x-library.layout-1col>` | `components/library/layout-1col` | slot | 单栏容器 |
+| `<x-library.layout-2col>` | `components/library/layout-2col` | slot `main` `aside` | 双栏容器 |
+| `<x-library.layout-3col>` | `components/library/layout-3col` | slot `left` `main` `right` | 三栏容器 |
+
+---
+
+## 10. 搜索规范
+
+搜索底层统一使用 OpenSearch,结果页统一为 `library/search.blade.php`。
+
+| 参数 | 说明 | 示例 |
+|---|---|---|
+| `q` | 搜索关键词 | `?q=dukkha` |
+| `type` | 栏目过滤 | `?type=wiki` `?type=tipitaka` |
+| `lang` | 语言过滤 | `?lang=zh` |
+
+各栏目的搜索入口(如 wiki 首页的搜索框)跳转至统一搜索页,并预填 `type` 参数。搜索提示下拉由 `search-suggest.js` 处理,调用各自的 suggest API。
+
+---
+
+## 11. 迁移策略
+
+重构为**增量迁移**,不一次性切换,降低风险。
+
+1. **以 wiki 为基准**:wiki 现有 CSS 和组件作为公共层的起点
+2. **提取公共层**:将 wiki 中可复用的部分移至 `layouts/`、`components/ui/`、`base/`
+3. **wiki 改为引用公共层**:行为不变,验证通过后继续
+4. **逐栏目迁移顺序**:wiki → anthology → tipitaka → blog(从简单到复杂)
+5. **旧文件保留至迁移完成**:`app.css`、各栏目旧 `layouts/app.blade.php` 在对应栏目迁移完成后再删除
+
+---
+
+*本文档随重构进展持续更新*

+ 0 - 11
api-v12/resources/css/app.css

@@ -1,11 +0,0 @@
-@import 'tailwindcss';
-
-@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
-@source '../../storage/framework/views/*.php';
-@source '../**/*.blade.php';
-@source '../**/*.js';
-
-@theme {
-    --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
-        'Segoe UI Symbol', 'Noto Color Emoji';
-}

+ 16 - 0
api-v12/resources/css/base/_reset.css

@@ -0,0 +1,16 @@
+/* resources/css/base/_reset.css
+   最小化 reset,补充 Tabler 未覆盖的部分。
+   不覆盖 Tabler 已处理好的规则。
+*/
+
+*,
+*::before,
+*::after {
+    box-sizing: border-box;
+}
+
+img,
+video {
+    max-width: 100%;
+    height: auto;
+}

+ 13 - 0
api-v12/resources/css/base/_typography.css

@@ -0,0 +1,13 @@
+/* resources/css/base/_typography.css
+   全站基础排版。
+   Noto Serif 已在 library.css 入口处 @import,此处只设置使用规则。
+*/
+
+/* 正文衬线字体作用域由各 module 自行声明(如 .wiki-content-body)。
+   全站默认保持 Tabler 的 sans-serif 体系,不在此全局覆盖。 */
+
+/* 标题基础 */
+h1, h2, h3, h4, h5, h6 {
+    line-height: 1.3;
+    font-weight: 600;
+}

+ 39 - 0
api-v12/resources/css/base/_variables.css

@@ -0,0 +1,39 @@
+/* resources/css/base/_variables.css
+   全站 CSS 变量
+   - 第一层:覆盖 Tabler 默认值
+   - 第二层:WikiPali 自定义 token
+   所有颜色、间距、字体 token 统一在此定义,各 module 只引用变量,不硬编码色值
+*/
+
+/* ── Tabler 变量覆盖 ── */
+:root {
+    --tblr-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+    --tblr-border-radius: 0.375rem;
+    --tblr-border-radius-lg: 0.5rem;
+}
+
+/* ── WikiPali 品牌色 token ── */
+:root {
+    /* 主色:暖金/琥珀,源自 main.css --sf */
+    --wp-brand:        #c8860a;
+    --wp-brand-light:  #f5e6c8;
+    --wp-brand-pale:   #fdf8f0;
+    --wp-brand-dark:   #9a6508;
+
+    /* 文字层级,源自 main.css --ink */
+    --wp-ink:          #1a1208;
+    --wp-ink-soft:     #4a3f2f;
+    --wp-ink-muted:    #8a7a68;
+
+    /* 边框 / 背景 */
+    --wp-border:       #e8ddd0;
+    --wp-card-bg:      #fffdf9;
+    --wp-surface-alt:  #fdf8f0;
+}
+
+/* ── 阅读页专属 token(reader.css 会用到) ── */
+:root {
+    --wp-reader-font-size:    1rem;
+    --wp-reader-line-height:  1.875;
+    --wp-reader-max-width:    720px;
+}

+ 4 - 0
api-v12/resources/css/components/_badge.css

@@ -0,0 +1,4 @@
+/* resources/css/components/_badge.css
+   通用徽章。wiki 质量徽章(.wiki-quality-badge)在 modules/_wiki.css 中定义。
+   此文件预留全站徽章扩展。
+*/

+ 23 - 0
api-v12/resources/css/components/_card-book.css

@@ -0,0 +1,23 @@
+/* resources/css/components/_card-book.css
+   书籍卡片组件。支持 vertical(封面上、标题下)和 horizontal(封面左、标题右)两种布局。
+   来源:main.css 的 .book-card / .book-cover 段落。
+*/
+
+.book-card {
+    transition: transform 0.2s;
+}
+
+.book-card:hover {
+    transform: translateY(-2px);
+}
+
+.book-cover {
+    height: 200px;
+    object-fit: cover;
+}
+
+@media (max-width: 768px) {
+    .book-cover {
+        height: 150px;
+    }
+}

+ 5 - 0
api-v12/resources/css/components/_card.css

@@ -0,0 +1,5 @@
+/* resources/css/components/_card.css
+   通用卡片(title + list 结构)。
+   wiki 专属卡片(.wiki-card、.wiki-sidebar-section)在 modules/_wiki.css 中定义。
+   此文件预留其他栏目卡片扩展。
+*/

+ 64 - 0
api-v12/resources/css/components/_pagination.css

@@ -0,0 +1,64 @@
+/* resources/css/components/_pagination.css
+   全站通用分页组件。
+   来源:wiki.css / wiki-search.css 的 .wiki-pagination 段落(两处相同,已去重)。
+*/
+
+.wiki-pagination {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 4px;
+    padding: 1.5rem 0 0.5rem;
+    flex-wrap: wrap;
+}
+
+.wiki-page-btn {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    min-width: 34px;
+    height: 34px;
+    padding: 0 6px;
+    border-radius: var(--tblr-border-radius);
+    border: 1px solid var(--tblr-border-color);
+    font-size: 0.875rem;
+    color: var(--tblr-body-color);
+    text-decoration: none;
+    background: var(--tblr-bg-surface);
+    transition: background 0.12s, border-color 0.12s;
+    user-select: none;
+}
+
+.wiki-page-btn:hover:not(.wiki-page-btn--active):not(.wiki-page-btn--disabled) {
+    background: var(--tblr-bg-surface-secondary);
+    border-color: var(--tblr-border-color-dark, #adb5bd);
+    color: var(--tblr-body-color);
+    text-decoration: none;
+}
+
+.wiki-page-btn--active {
+    background: var(--tblr-primary);
+    border-color: var(--tblr-primary);
+    color: #fff;
+    font-weight: 500;
+    cursor: default;
+    pointer-events: none;
+}
+
+.wiki-page-btn--disabled {
+    color: var(--tblr-secondary);
+    cursor: default;
+    pointer-events: none;
+    opacity: 0.5;
+}
+
+.wiki-page-ellipsis {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    min-width: 28px;
+    height: 34px;
+    font-size: 0.875rem;
+    color: var(--tblr-secondary);
+    user-select: none;
+}

+ 13 - 0
api-v12/resources/css/components/_search-input.css

@@ -0,0 +1,13 @@
+/* resources/css/components/_search-input.css
+   搜索输入框 + 提示下拉。
+   主要样式由 Tabler input-group 提供,此处补充 WikiPali 专属覆盖。
+*/
+
+.search-box {
+    background: white;
+    border-radius: 0.5rem;
+    padding: 0.5rem;
+    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+    max-width: 500px;
+    margin: 0 auto;
+}

+ 9 - 84
api-v12/resources/css/wiki-search.css → api-v12/resources/css/components/_search-results.css

@@ -1,4 +1,8 @@
-/* ── 追加到 resources/css/wiki.css 末尾 ── */
+/* resources/css/components/_search-results.css
+   统一搜索结果页组件样式。
+   来源:wiki-search.css(与 wiki.css 末尾重复内容已去重,以 wiki 栏目为准)。
+   适用于 /library/search?type= 所有类型。
+*/
 
 
 /* ── 搜索栏 ── */
 /* ── 搜索栏 ── */
 .wiki-search-bar-wrap {
 .wiki-search-bar-wrap {
@@ -24,7 +28,7 @@
 
 
 /* ── 搜索结果列表容器 ── */
 /* ── 搜索结果列表容器 ── */
 .wiki-search-results {
 .wiki-search-results {
-    padding: 0;         /* 覆盖 wiki-card 默认 padding,由卡片自身管理间距 */
+    padding: 0;
 }
 }
 
 
 /* ── 搜索结果卡片 ── */
 /* ── 搜索结果卡片 ── */
@@ -64,7 +68,7 @@
 }
 }
 
 
 .wiki-search-card-word {
 .wiki-search-card-word {
-    font-family: 'Noto Serif', Georgia, serif;
+    font-family: "Noto Serif", Georgia, serif;
     font-size: 0.875rem;
     font-size: 0.875rem;
     font-style: italic;
     font-style: italic;
     color: var(--tblr-secondary);
     color: var(--tblr-secondary);
@@ -78,10 +82,9 @@
     margin: 0 0 6px;
     margin: 0 0 6px;
 }
 }
 
 
-/* highlight 高亮词 */
 .wiki-search-card-snippet mark {
 .wiki-search-card-snippet mark {
-    background: #FAEEDA;
-    color: #854F0B;
+    background: #faeeda;
+    color: #854f0b;
     padding: 1px 2px;
     padding: 1px 2px;
     border-radius: 3px;
     border-radius: 3px;
     font-style: normal;
     font-style: normal;
@@ -99,23 +102,6 @@
     color: var(--tblr-border-color-dark, #adb5bd);
     color: var(--tblr-border-color-dark, #adb5bd);
 }
 }
 
 
-/* ── 分类筛选 badge ── */
-.wiki-cat-count {
-    font-size: 0.6875rem;
-    background: var(--tblr-bg-surface-secondary);
-    border: 1px solid var(--tblr-border-color);
-    border-radius: 20px;
-    padding: 1px 7px;
-    color: var(--tblr-secondary);
-    margin-left: auto;
-    flex-shrink: 0;
-}
-
-.wiki-cat-list a {
-    display: flex;           /* 覆盖原 block,让 count badge 右对齐 */
-    align-items: center;
-}
-
 /* ── 空状态 ── */
 /* ── 空状态 ── */
 .wiki-empty-state {
 .wiki-empty-state {
     text-align: center;
     text-align: center;
@@ -150,64 +136,3 @@
     color: var(--tblr-primary);
     color: var(--tblr-primary);
     text-decoration: none;
     text-decoration: none;
 }
 }
-
-/* ── 分页 ── */
-.wiki-pagination {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    gap: 4px;
-    padding: 1.5rem 0 0.5rem;
-    flex-wrap: wrap;
-}
-
-.wiki-page-btn {
-    display: inline-flex;
-    align-items: center;
-    justify-content: center;
-    min-width: 34px;
-    height: 34px;
-    padding: 0 6px;
-    border-radius: var(--tblr-border-radius);
-    border: 1px solid var(--tblr-border-color);
-    font-size: 0.875rem;
-    color: var(--tblr-body-color);
-    text-decoration: none;
-    background: var(--tblr-bg-surface);
-    transition: background 0.12s, border-color 0.12s;
-    user-select: none;
-}
-
-.wiki-page-btn:hover:not(.wiki-page-btn--active):not(.wiki-page-btn--disabled) {
-    background: var(--tblr-bg-surface-secondary);
-    border-color: var(--tblr-border-color-dark, #adb5bd);
-    color: var(--tblr-body-color);
-    text-decoration: none;
-}
-
-.wiki-page-btn--active {
-    background: var(--tblr-primary);
-    border-color: var(--tblr-primary);
-    color: #fff;
-    font-weight: 500;
-    cursor: default;
-    pointer-events: none;
-}
-
-.wiki-page-btn--disabled {
-    color: var(--tblr-secondary);
-    cursor: default;
-    pointer-events: none;
-    opacity: 0.5;
-}
-
-.wiki-page-ellipsis {
-    display: inline-flex;
-    align-items: center;
-    justify-content: center;
-    min-width: 28px;
-    height: 34px;
-    font-size: 0.875rem;
-    color: var(--tblr-secondary);
-    user-select: none;
-}

+ 92 - 0
api-v12/resources/css/layout/_drawer.css

@@ -0,0 +1,92 @@
+/* resources/css/layout/_drawer.css
+   右侧 mobile 导航抽屉(nav drawer)。
+   来源:main.css 的 .bc-mobile-overlay / .bc-mobile-drawer 段落。
+
+   左侧内容抽屉(TOC、侧边栏)复用 Tabler Offcanvas,
+   通过 <x-ui.drawer side="start|end"> 组件控制方向,无需额外 CSS。
+*/
+
+/* ── 遮罩 ── */
+.bc-mobile-overlay {
+    display: none;
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.4);
+    z-index: 1040;
+}
+
+.bc-mobile-overlay.open {
+    display: block;
+}
+
+/* ── 抽屉面板 ── */
+.bc-mobile-drawer {
+    position: fixed;
+    top: 0;
+    right: -100%;
+    width: 240px;
+    height: 100vh;
+    background: var(--wp-card-bg);
+    border-left: 1px solid var(--wp-border);
+    z-index: 1050;
+    transition: right 0.25s ease;
+    padding: 1rem 0;
+    box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
+}
+
+.bc-mobile-drawer.open {
+    right: 0;
+}
+
+/* ── 抽屉头部 ── */
+.bc-mobile-drawer-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0.5rem 1.25rem 0.75rem;
+    border-bottom: 1px solid var(--wp-border);
+    margin-bottom: 0.5rem;
+}
+
+.bc-mobile-drawer-header span {
+    font-size: 0.85rem;
+    font-weight: 600;
+    color: var(--wp-ink-soft);
+}
+
+.bc-mobile-drawer-close {
+    background: none;
+    border: none;
+    cursor: pointer;
+    color: var(--wp-ink-muted);
+    font-size: 1rem;
+    line-height: 1;
+    padding: 2px;
+}
+
+/* ── 抽屉导航链接 ── */
+.bc-mobile-nav {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.bc-mobile-nav li a {
+    display: block;
+    padding: 0.65rem 1.25rem;
+    font-size: 0.9rem;
+    color: var(--wp-ink-soft);
+    text-decoration: none;
+    border-bottom: 1px solid rgba(232, 221, 208, 0.5);
+    transition: background 0.15s;
+}
+
+.bc-mobile-nav li a:hover {
+    background: var(--wp-surface-alt);
+    color: var(--wp-brand);
+}
+
+.bc-mobile-nav li a.active {
+    color: var(--wp-brand);
+    font-weight: 600;
+}

+ 4 - 0
api-v12/resources/css/layout/_footer.css

@@ -0,0 +1,4 @@
+/* resources/css/layout/_footer.css
+   全站 footer 样式。
+   当前 library 无独立 footer,此文件预留,待 footer 组件建立后填充。
+*/

+ 46 - 0
api-v12/resources/css/layout/_grid.css

@@ -0,0 +1,46 @@
+/* resources/css/layout/_grid.css
+   全站断点定义 + 通用栏容器。
+   各栏目三栏/两栏 grid 在此定义基础变量,具体 grid-template 在各 module 实现。
+*/
+
+/* ── 断点 token(与 Tabler Bootstrap 保持一致) ── */
+:root {
+    --bp-sm:  576px;
+    --bp-md:  768px;
+    --bp-lg:  992px;
+    --bp-xl:  1200px;
+}
+
+/* ── 通用两栏容器 ── */
+.layout-2col {
+    display: grid;
+    grid-template-columns: 220px 1fr;
+    gap: 1.5rem;
+    align-items: start;
+    padding-top: 1.5rem;
+    padding-bottom: 3rem;
+}
+
+/* ── 通用三栏容器 ── */
+.layout-3col {
+    display: grid;
+    grid-template-columns: 200px 1fr 200px;
+    gap: 1.5rem;
+    align-items: start;
+    padding-top: 1.5rem;
+    padding-bottom: 3rem;
+}
+
+/* ── 响应式降级 ── */
+@media (max-width: 992px) {
+    .layout-3col {
+        grid-template-columns: 180px 1fr;
+    }
+}
+
+@media (max-width: 768px) {
+    .layout-2col,
+    .layout-3col {
+        grid-template-columns: 1fr;
+    }
+}

+ 76 - 0
api-v12/resources/css/layout/_hero.css

@@ -0,0 +1,76 @@
+/* resources/css/layout/_hero.css
+   Hero 区域(封面图 + 叠加层 + 标题文字)。
+   来源:main.css 的 .hero-section、.hero-overlay、.hero-content 段落。
+*/
+
+.hero-wrapper {
+    position: relative;
+}
+
+.hero-section {
+    height: 250px;
+    width: 100%;
+    background-size: cover;
+    background-position: center;
+    background-repeat: no-repeat;
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.hero-overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.2);
+}
+
+.hero-content {
+    position: relative;
+    z-index: 2;
+    text-align: center;
+    color: white;
+    max-width: 600px;
+    padding: 0 1rem;
+}
+
+.hero-title {
+    font-size: 2.5rem;
+    font-weight: bold;
+    margin-bottom: 1rem;
+    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
+}
+
+.hero-subtitle {
+    font-size: 1.2rem;
+    margin-bottom: 2rem;
+    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
+}
+
+/* ── 响应式 ── */
+@media (max-width: 768px) {
+    .hero-section {
+        height: 250px;
+    }
+
+    .hero-title {
+        font-size: 2rem;
+    }
+
+    .hero-subtitle {
+        font-size: 1rem;
+    }
+}
+
+@media (max-width: 576px) {
+    .hero-title {
+        font-size: 1.5rem;
+    }
+
+    .hero-subtitle {
+        font-size: 0.9rem;
+    }
+}

+ 119 - 0
api-v12/resources/css/layout/_navbar.css

@@ -0,0 +1,119 @@
+/* resources/css/layout/_navbar.css
+   顶部导航栏:面包屑 bar + desktop 导航链接 + mobile 汉堡按钮。
+   来源:main.css 的 .anthology-breadcrumb-bar、.bc-nav、.bc-hamburger 段落。
+   以 wiki.css 中同名规则为准(main.css 无冲突,wiki.css 中无同名规则,直接迁移)。
+*/
+
+/* ── Breadcrumb bar ── */
+.anthology-breadcrumb-bar {
+    background: rgba(255, 255, 255, 0.55);
+    border-bottom: 1px solid var(--wp-border);
+    padding: 0.5rem 0;
+}
+
+.anthology-breadcrumb-bar .bc-inner {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 1rem;
+}
+
+.anthology-breadcrumb-bar .breadcrumb {
+    margin: 0;
+    font-size: 0.78rem;
+    flex-shrink: 0;
+}
+
+.anthology-breadcrumb-bar .breadcrumb-item a {
+    color: var(--wp-brand);
+    text-decoration: none;
+}
+
+.anthology-breadcrumb-bar .breadcrumb-item.active {
+    color: var(--wp-ink-muted);
+}
+
+.anthology-breadcrumb-bar .breadcrumb-item + .breadcrumb-item::before {
+    color: var(--wp-ink-muted);
+}
+
+/* ── Hero 覆盖状态:breadcrumb bar 透明悬浮于 hero 之上 ── */
+.hero-wrapper:has(.hero-section) .anthology-breadcrumb-bar {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    z-index: 10;
+    background: transparent;
+    border-bottom: none;
+}
+
+.hero-wrapper:has(.hero-section) .bc-nav li a {
+    color: white;
+}
+
+.hero-wrapper:has(.hero-section) .bc-nav li a:hover {
+    color: rgba(255, 255, 255, 0.75);
+}
+
+.hero-wrapper:has(.hero-section) .bc-hamburger {
+    border-color: rgba(255, 255, 255, 0.6);
+    color: white;
+}
+
+/* ── Desktop 导航链接 ── */
+.bc-nav {
+    display: flex;
+    align-items: center;
+    gap: 1.25rem;
+    list-style: none;
+    margin: 0;
+    padding: 0;
+    flex-shrink: 0;
+}
+
+.bc-nav li a {
+    font-size: 0.82rem;
+    color: var(--wp-ink-soft);
+    text-decoration: none;
+    white-space: nowrap;
+    transition: color 0.15s;
+}
+
+.bc-nav li a:hover {
+    color: var(--wp-brand);
+}
+
+.bc-nav li a.active {
+    color: var(--wp-brand);
+    font-weight: 600;
+}
+
+/* ── 汉堡按钮(mobile) ── */
+.bc-hamburger {
+    display: none;
+    background: none;
+    border: 1px solid var(--wp-border);
+    border-radius: 5px;
+    padding: 4px 8px;
+    cursor: pointer;
+    color: var(--wp-ink-soft);
+    line-height: 1;
+}
+
+.bc-hamburger:hover {
+    border-color: var(--wp-brand);
+    color: var(--wp-brand);
+}
+
+/* ── 响应式 ── */
+@media (max-width: 640px) {
+    .bc-nav {
+        display: none;
+    }
+
+    .bc-hamburger {
+        display: inline-flex;
+        align-items: center;
+    }
+}

+ 4 - 0
api-v12/resources/css/layout/_toolbar.css

@@ -0,0 +1,4 @@
+/* resources/css/layout/_toolbar.css
+   页内工具条(过滤器、排序、批量操作等)。
+   当前以 Tabler .page-header 为基础,此文件预留扩展。
+*/

+ 33 - 0
api-v12/resources/css/library.css

@@ -0,0 +1,33 @@
+/* resources/css/library.css
+   library/* 页面 CSS 入口。
+   只做 @import,不写任何样式规则。
+   各栏目专属样式通过页面级 @push('styles') 按需追加。
+*/
+
+/* 1. Tabler 核心 */
+@import "@tabler/core/dist/css/tabler.min.css";
+@import "@tabler/icons-webfont/dist/tabler-icons.min.css";
+
+/* 2. 全站字体 */
+@import url("https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,600;1,400&display=swap");
+
+/* 3. 基础变量 */
+@import "./base/_variables.css";
+@import "./base/_reset.css";
+@import "./base/_typography.css";
+
+/* 4. 布局层 */
+@import "./layout/_grid.css";
+@import "./layout/_navbar.css";
+@import "./layout/_drawer.css";
+@import "./layout/_hero.css";
+@import "./layout/_footer.css";
+@import "./layout/_toolbar.css";
+
+/* 5. 公共组件 */
+@import "./components/_search-input.css";
+@import "./components/_search-results.css";
+@import "./components/_card.css";
+@import "./components/_card-book.css";
+@import "./components/_badge.css";
+@import "./components/_pagination.css";

+ 997 - 0
api-v12/resources/css/modules/_wiki.css

@@ -0,0 +1,997 @@
+/* resources/css/modules/_wiki.css
+   Wiki 栏目专属样式。
+   来源:
+     - wiki.css(布局、侧边栏、组件、term popover/drawer)
+     - wiki-content.css(正文排版,作用域 .wiki-content-body)
+   去重:wiki-search.css / wiki.css 末尾重复内容已移至 components/_search-results.css
+         wiki-search.css / wiki.css 末尾重复的分页已移至 components/_pagination.css
+*/
+
+/* ══════════════════════════════════════════
+   一、三栏布局
+   ══════════════════════════════════════════ */
+
+.wiki-layout {
+    display: grid;
+    grid-template-columns: 200px 1fr 200px;
+    grid-template-areas: "left main right";
+    gap: 1.5rem;
+    align-items: start;
+    padding-top: 1.5rem;
+    padding-bottom: 3rem;
+}
+
+.wiki-sidebar-left {
+    grid-area: left;
+}
+.wiki-sidebar-right {
+    grid-area: right;
+}
+
+.wiki-main {
+    grid-area: main;
+    min-width: 0;
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
+}
+
+/* ══════════════════════════════════════════
+   二、通用卡片 / 侧边栏
+   ══════════════════════════════════════════ */
+
+.wiki-card {
+    background: var(--tblr-bg-surface);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: var(--tblr-border-radius-lg);
+    padding: 1.5rem;
+}
+
+.wiki-sidebar-section {
+    background: var(--tblr-bg-surface);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: var(--tblr-border-radius-lg);
+    padding: 1rem 1.125rem;
+    margin-bottom: 1rem;
+}
+
+.wiki-sidebar-title {
+    font-size: 0.6875rem;
+    font-weight: 500;
+    letter-spacing: 0.05em;
+    text-transform: uppercase;
+    color: var(--tblr-secondary);
+    margin-bottom: 0.75rem;
+}
+
+/* ── 分类列表 ── */
+.wiki-cat-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.wiki-cat-list li {
+    margin-bottom: 2px;
+}
+
+.wiki-cat-list a {
+    display: flex;
+    align-items: center;
+    font-size: 0.8125rem;
+    color: var(--tblr-body-color);
+    text-decoration: none;
+    padding: 5px 8px;
+    border-radius: var(--tblr-border-radius);
+    transition: background 0.12s;
+}
+
+.wiki-cat-list a:hover {
+    background: var(--tblr-bg-surface-secondary);
+}
+.wiki-cat-list a.active {
+    background: var(--tblr-bg-surface-secondary);
+    font-weight: 500;
+    color: var(--tblr-primary);
+}
+
+.wiki-cat-count {
+    font-size: 0.6875rem;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 20px;
+    padding: 1px 7px;
+    color: var(--tblr-secondary);
+    margin-left: auto;
+    flex-shrink: 0;
+}
+
+/* ══════════════════════════════════════════
+   三、目录(TOC)
+   ══════════════════════════════════════════ */
+
+.wiki-toc-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.wiki-toc-list li {
+    border-bottom: 1px solid var(--tblr-border-color);
+}
+
+.wiki-toc-list li:last-child {
+    border-bottom: none;
+}
+
+.wiki-toc-list a {
+    display: block;
+    font-size: 0.8125rem;
+    color: var(--tblr-secondary);
+    text-decoration: none;
+    padding: 5px 0;
+    transition: color 0.12s;
+}
+
+.wiki-toc-list a:hover {
+    color: var(--tblr-body-color);
+}
+.wiki-toc-list a.active {
+    color: var(--tblr-body-color);
+    font-weight: 500;
+}
+
+.wiki-toc-list .toc-level-2 a {
+    padding-left: 0.5rem;
+}
+.wiki-toc-list .toc-level-3 a {
+    padding-left: 1rem;
+}
+
+.wiki-toc-num {
+    color: var(--tblr-secondary);
+    margin-right: 5px;
+    font-size: 0.75rem;
+}
+
+/* ══════════════════════════════════════════
+   四、相关条目
+   ══════════════════════════════════════════ */
+
+.wiki-related-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.wiki-related-list li {
+    border-bottom: 1px solid var(--tblr-border-color);
+}
+
+.wiki-related-list li:last-child {
+    border-bottom: none;
+}
+
+.wiki-related-list a {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: 0.8125rem;
+    color: var(--tblr-primary);
+    text-decoration: none;
+    padding: 6px 0;
+}
+
+.wiki-related-zh {
+    font-size: 0.75rem;
+    color: var(--tblr-secondary);
+}
+
+/* ══════════════════════════════════════════
+   五、元信息表格
+   ══════════════════════════════════════════ */
+
+.wiki-meta-table {
+    width: 100%;
+    font-size: 0.8125rem;
+    border-collapse: collapse;
+}
+
+.wiki-meta-table td {
+    padding: 3px 0;
+}
+.wiki-meta-table td:last-child {
+    text-align: right;
+    color: var(--tblr-secondary);
+}
+
+/* ══════════════════════════════════════════
+   六、条目头部
+   ══════════════════════════════════════════ */
+
+.wiki-entry-header {
+    margin-bottom: 1.25rem;
+}
+
+.wiki-entry-title {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 1.75rem;
+    font-weight: 600;
+    line-height: 1.25;
+    margin: 0.375rem 0 0.75rem;
+    color: var(--tblr-body-color);
+}
+
+.wiki-entry-langs-inline {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 6px;
+    margin-bottom: 0.5rem;
+}
+
+.wiki-lang-pill {
+    font-size: 0.75rem;
+    padding: 3px 10px;
+    border-radius: 20px;
+    border: 1px solid var(--tblr-border-color);
+    color: var(--tblr-secondary);
+}
+
+.wiki-lang-pill strong {
+    font-weight: 500;
+    color: var(--tblr-body-color);
+    margin-right: 3px;
+}
+
+/* ── 语言版本切换 ── */
+.wiki-entry-lang-switcher {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 6px;
+    padding: 0.625rem 0.875rem;
+    background: var(--tblr-bg-surface-secondary);
+    border-radius: var(--tblr-border-radius);
+    margin-bottom: 1.25rem;
+    font-size: 0.8125rem;
+}
+
+.wiki-entry-lang-label {
+    color: var(--tblr-secondary);
+}
+
+.wiki-entry-lang-btn {
+    font-size: 0.75rem;
+    padding: 3px 10px;
+    border-radius: 20px;
+    border: 1px solid var(--tblr-border-color);
+    color: var(--tblr-primary);
+    text-decoration: none;
+    transition: background 0.12s;
+}
+
+.wiki-entry-lang-btn:hover {
+    background: var(--tblr-bg-surface);
+}
+
+/* ══════════════════════════════════════════
+   七、质量标签
+   ══════════════════════════════════════════ */
+
+.wiki-quality-badge {
+    display: inline-flex;
+    align-items: center;
+    gap: 5px;
+    font-size: 0.6875rem;
+    padding: 3px 10px;
+    border-radius: 20px;
+    font-weight: 500;
+    margin-bottom: 4px;
+}
+
+.wiki-quality-dot {
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    flex-shrink: 0;
+}
+
+.wiki-badge--featured {
+    background: #eaf3de;
+    color: #3b6d11;
+    border: 1px solid #c0dd97;
+}
+.wiki-badge--featured .wiki-quality-dot {
+    background: #639922;
+}
+
+.wiki-badge--review {
+    background: #faeeda;
+    color: #854f0b;
+    border: 1px solid #fac775;
+}
+.wiki-badge--review .wiki-quality-dot {
+    background: #ba7517;
+}
+
+.wiki-badge--stub {
+    background: #f1efe8;
+    color: #5f5e5a;
+    border: 1px solid #d3d1c7;
+}
+.wiki-badge--stub .wiki-quality-dot {
+    background: #888780;
+}
+
+/* ══════════════════════════════════════════
+   八、标签
+   ══════════════════════════════════════════ */
+
+.wiki-tags {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 6px;
+    padding-top: 1rem;
+    border-top: 1px solid var(--tblr-border-color);
+    margin-top: 1.5rem;
+}
+
+.wiki-tag {
+    font-size: 0.75rem;
+    padding: 3px 10px;
+    border-radius: 20px;
+    border: 1px solid var(--tblr-border-color);
+    color: var(--tblr-secondary);
+    text-decoration: none;
+    transition: background 0.12s;
+}
+
+.wiki-tag:hover {
+    background: var(--tblr-bg-surface-secondary);
+    color: var(--tblr-body-color);
+}
+
+/* ══════════════════════════════════════════
+   九、首页组件
+   ══════════════════════════════════════════ */
+
+/* 今日条目 */
+.wiki-today-banner {
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: var(--tblr-border-radius-lg);
+    padding: 1.25rem 1.5rem;
+    display: flex;
+    gap: 1.25rem;
+    align-items: flex-start;
+}
+
+.wiki-today-icon {
+    width: 48px;
+    height: 48px;
+    border-radius: var(--tblr-border-radius);
+    background: #e1f5ee;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 1.375rem;
+    flex-shrink: 0;
+}
+
+.wiki-today-label {
+    font-size: 0.6875rem;
+    text-transform: uppercase;
+    letter-spacing: 0.05em;
+    color: var(--tblr-secondary);
+    margin-bottom: 4px;
+}
+
+.wiki-today-title {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 1.125rem;
+    font-weight: 600;
+    margin-bottom: 6px;
+    color: var(--tblr-body-color);
+}
+
+.wiki-today-snippet {
+    font-size: 0.875rem;
+    color: var(--tblr-secondary);
+    line-height: 1.6;
+}
+
+.wiki-today-link {
+    font-size: 0.8125rem;
+    color: var(--tblr-primary);
+    text-decoration: none;
+    margin-top: 10px;
+    display: inline-block;
+}
+
+/* 精选网格 */
+.wiki-featured-grid {
+    display: grid;
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+    gap: 8px;
+}
+
+.wiki-featured-card {
+    border: 1px solid var(--tblr-border-color);
+    border-radius: var(--tblr-border-radius);
+    padding: 10px 12px;
+    cursor: pointer;
+    text-decoration: none;
+    display: block;
+    transition: background 0.12s;
+    color: var(--tblr-body-color);
+}
+
+.wiki-featured-card:hover {
+    background: var(--tblr-bg-surface-secondary);
+}
+
+.wiki-featured-label {
+    font-size: 0.6875rem;
+    font-weight: 500;
+    text-transform: uppercase;
+    letter-spacing: 0.05em;
+    color: var(--tblr-secondary);
+    margin-bottom: 5px;
+}
+
+.wiki-featured-title {
+    font-size: 0.875rem;
+    font-weight: 500;
+    margin-bottom: 2px;
+}
+
+.wiki-featured-pali {
+    font-size: 0.75rem;
+    font-style: italic;
+    color: var(--tblr-secondary);
+}
+
+/* ══════════════════════════════════════════
+   十、term-ref 行内术语标记
+   ══════════════════════════════════════════ */
+
+.term-ref {
+    font-style: italic;
+    color: var(--tblr-primary);
+    border-bottom: 1px dotted var(--tblr-primary);
+    cursor: pointer;
+}
+
+/* ══════════════════════════════════════════
+   十一、Term Popover(桌面)
+   ══════════════════════════════════════════ */
+
+.wiki-term-popover.popover {
+    max-width: 280px;
+    font-size: 0.8125rem;
+    border: 0.5px solid var(--tblr-border-color);
+    border-radius: var(--tblr-border-radius-lg);
+    box-shadow: none;
+}
+
+.wiki-term-popover .popover-arrow {
+    display: none;
+}
+.wiki-term-popover .popover-body {
+    padding: 0;
+}
+
+.wiki-popover-word {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 0.9375rem;
+    font-style: italic;
+    font-weight: 600;
+    color: var(--tblr-primary);
+    padding: 10px 14px;
+    border-bottom: 0.5px solid var(--tblr-border-color);
+}
+
+.wiki-popover-meaning {
+    font-size: 0.9rem;
+    font-weight: 500;
+    color: var(--tblr-body-color);
+    padding: 8px 14px 4px;
+}
+
+.wiki-popover-summary {
+    font-size: 0.8125rem;
+    color: var(--tblr-secondary);
+    line-height: 1.55;
+    padding: 0 14px 12px;
+}
+
+/* ══════════════════════════════════════════
+   十二、Term Drawer(移动端 Offcanvas)
+   所有阅读页公用,此处定义,reader.css 通过 @import 引入
+   ══════════════════════════════════════════ */
+
+.wiki-term-drawer {
+    border-radius: 1rem 1rem 0 0;
+    max-height: 55vh;
+}
+
+.wiki-term-drawer .offcanvas-header {
+    padding-top: 0.6rem;
+    padding-bottom: 0.6rem;
+}
+
+.wiki-drawer-word {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 1.125rem;
+    font-style: italic;
+    font-weight: 600;
+    color: var(--tblr-primary);
+}
+
+.wiki-drawer-meaning {
+    font-size: 0.9375rem;
+    font-weight: 500;
+    color: var(--tblr-body-color);
+    margin-top: 2px;
+}
+
+.wiki-drawer-summary {
+    font-size: 0.9375rem;
+    color: var(--tblr-body-color);
+    line-height: 1.65;
+}
+
+.wiki-drawer-link {
+    display: inline-block;
+    margin-top: 1rem;
+    font-size: 0.875rem;
+    color: var(--tblr-primary);
+    text-decoration: none;
+}
+
+/* ── term-card 共享(Popover + Drawer 复用) ── */
+.wiki-term-card-word {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 0.9375rem;
+    font-style: italic;
+    font-weight: 600;
+    color: var(--tblr-primary);
+    padding: 8px;
+    margin-bottom: 8px;
+    border-bottom: 0.5px solid var(--tblr-border-color);
+}
+
+.wiki-term-card-meaning {
+    font-size: 0.875rem;
+    font-weight: 500;
+    color: var(--tblr-body-color);
+    margin-bottom: 5px;
+}
+
+.wiki-term-card-summary {
+    font-size: 0.8125rem;
+    color: var(--tblr-secondary);
+    line-height: 1.55;
+}
+
+/* ── Skeleton loading ── */
+.wiki-term-skeleton-word,
+.wiki-term-skeleton-line {
+    background: linear-gradient(90deg, #e8e8e8 25%, #f5f5f5 50%, #e8e8e8 75%);
+    background-size: 200% 100%;
+    animation: wiki-skeleton-shimmer 1.2s infinite;
+    border-radius: 4px;
+}
+
+.wiki-term-skeleton-word {
+    height: 18px;
+    width: 120px;
+    margin-bottom: 8px;
+}
+.wiki-term-skeleton-line {
+    height: 13px;
+    width: 100%;
+    margin-bottom: 6px;
+}
+.wiki-term-skeleton-line.short {
+    width: 60%;
+}
+
+@keyframes wiki-skeleton-shimmer {
+    0% {
+        background-position: 200% 0;
+    }
+    100% {
+        background-position: -200% 0;
+    }
+}
+
+/* ══════════════════════════════════════════
+   十三、正文排版(.wiki-content-body)
+   来源:wiki-content.css,作用域不变
+   ══════════════════════════════════════════ */
+
+.wiki-content-body {
+    font-family: "Noto Serif", Georgia, "Times New Roman", serif;
+    font-size: 1rem;
+    line-height: 1.875;
+    color: var(--tblr-body-color);
+    word-break: break-word;
+    overflow-wrap: break-word;
+}
+
+.wiki-content-body p {
+    margin-top: 0;
+    margin-bottom: 1.125em;
+}
+
+.wiki-content-body h1,
+.wiki-content-body h2,
+.wiki-content-body h3,
+.wiki-content-body h4,
+.wiki-content-body h5,
+.wiki-content-body h6 {
+    font-family: "Noto Serif", Georgia, serif;
+    font-weight: 600;
+    line-height: 1.3;
+    color: var(--tblr-body-color);
+    margin-top: 2em;
+    margin-bottom: 0.6em;
+    scroll-margin-top: 80px;
+}
+
+.wiki-content-body h1 {
+    font-size: 1.375rem;
+    padding-bottom: 0.4em;
+    border-bottom: 2px solid var(--tblr-border-color);
+}
+
+.wiki-content-body h2 {
+    font-size: 1.25rem;
+    padding-bottom: 0.4em;
+    border-bottom: 1px solid var(--tblr-border-color);
+}
+
+.wiki-content-body h3 {
+    font-size: 1.0625rem;
+}
+
+.wiki-content-body h4 {
+    font-size: 0.9375rem;
+    font-weight: 600;
+    color: var(--tblr-secondary);
+}
+
+.wiki-content-body strong,
+.wiki-content-body b {
+    font-weight: 600;
+}
+
+.wiki-content-body em,
+.wiki-content-body i {
+    font-style: italic;
+}
+
+.wiki-content-body a {
+    color: var(--tblr-primary);
+    text-decoration: none;
+    border-bottom: 1px solid transparent;
+    transition: border-color 0.12s;
+}
+
+.wiki-content-body a:hover {
+    border-bottom-color: var(--tblr-primary);
+}
+
+.wiki-content-body blockquote {
+    margin: 1.5em 0;
+    padding: 0.875rem 1.125rem;
+    border-left: 3px solid var(--tblr-border-color-dark, #adb5bd);
+    background: var(--tblr-bg-surface-secondary);
+    border-radius: 0 var(--tblr-border-radius) var(--tblr-border-radius) 0;
+    font-size: 0.9375rem;
+    line-height: 1.75;
+}
+
+.wiki-content-body blockquote p {
+    margin-bottom: 0.5em;
+}
+.wiki-content-body blockquote p:last-child {
+    margin-bottom: 0;
+}
+
+.wiki-content-body blockquote cite {
+    display: block;
+    margin-top: 0.625rem;
+    font-size: 0.8125rem;
+    font-style: normal;
+    color: var(--tblr-secondary);
+}
+
+.wiki-content-body ul,
+.wiki-content-body ol {
+    padding-left: 1.5em;
+    margin-bottom: 1.125em;
+}
+
+.wiki-content-body li {
+    margin-bottom: 0.375em;
+    line-height: 1.75;
+}
+
+.wiki-content-body table {
+    width: 100%;
+    border-collapse: collapse;
+    font-size: 0.9rem;
+    margin-bottom: 1.5em;
+}
+
+.wiki-content-body th {
+    font-family: "Noto Serif", Georgia, serif;
+    font-weight: 600;
+    font-size: 0.8125rem;
+    text-align: left;
+    padding: 8px 12px;
+    background: var(--tblr-bg-surface-secondary);
+    border-bottom: 2px solid var(--tblr-border-color);
+    color: var(--tblr-secondary);
+    text-transform: uppercase;
+    letter-spacing: 0.04em;
+}
+
+.wiki-content-body td {
+    padding: 8px 12px;
+    border-bottom: 1px solid var(--tblr-border-color);
+    vertical-align: top;
+}
+
+.wiki-content-body tr:last-child td {
+    border-bottom: none;
+}
+
+.wiki-content-body code {
+    font-family: var(
+        --tblr-font-monospace,
+        "SFMono-Regular",
+        Consolas,
+        monospace
+    );
+    font-size: 0.875em;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 4px;
+    padding: 1px 5px;
+}
+
+.wiki-content-body hr {
+    border: none;
+    border-top: 1px solid var(--tblr-border-color);
+    margin: 2em 0;
+}
+
+.wiki-content-body img {
+    max-width: 100%;
+    height: auto;
+    border-radius: var(--tblr-border-radius);
+    margin: 0.75em 0;
+}
+
+.wiki-content-body figure {
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+.wiki-content-body figcaption {
+    font-size: 0.8125rem;
+    color: var(--tblr-secondary);
+    margin-top: 0.5em;
+    font-style: italic;
+}
+
+/* ══════════════════════════════════════════
+   十四、响应式
+   ══════════════════════════════════════════ */
+
+@media (max-width: 992px) {
+    .wiki-layout {
+        grid-template-columns: 180px 1fr;
+        grid-template-areas:
+            "left main"
+            "left right";
+    }
+
+    .wiki-sidebar-right {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 1rem;
+    }
+
+    .wiki-sidebar-right .wiki-sidebar-section {
+        flex: 1 1 180px;
+        margin-bottom: 0;
+    }
+}
+
+@media (max-width: 768px) {
+    .wiki-layout {
+        grid-template-columns: 1fr;
+        grid-template-areas: "main" "right" "left";
+    }
+
+    .wiki-featured-grid {
+        grid-template-columns: repeat(2, minmax(0, 1fr));
+    }
+
+    .wiki-sidebar-left,
+    .wiki-sidebar-right {
+        display: none;
+    }
+}
+
+/* ══════════════════════════════════════════
+   十五、Wiki 首页(home.blade.php)
+   替代原 home.blade.php 内联 <style>,风格与 wiki 整体保持一致
+   ══════════════════════════════════════════ */
+
+.wiki-home-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    min-height: calc(100vh - 300px);
+    padding: 3rem 1.5rem;
+}
+
+/* ── 法轮图标 ── */
+.wiki-home-wheel {
+    margin-bottom: 1.5rem;
+}
+
+.wiki-home-wheel-img {
+    width: 120px;
+    height: auto;
+    opacity: 0.85;
+    transition: transform 0.3s ease;
+}
+
+.wiki-home-wheel-img:hover {
+    transform: scale(1.05);
+}
+
+/* ── 标题 ── */
+.wiki-home-title {
+    text-align: center;
+    margin-bottom: 2rem;
+}
+
+.wiki-home-title h1 {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 2.25rem;
+    font-weight: 600;
+    color: var(--tblr-body-color);
+    margin-bottom: 0.5rem;
+}
+
+/* ── 搜索框 ── */
+.wiki-home-search {
+    width: 100%;
+    max-width: 640px;
+    margin: 0 auto 1.5rem;
+}
+
+/* ── 热门标签 ── */
+.wiki-home-hot-tags {
+    text-align: center;
+    margin-bottom: 2.5rem;
+    font-size: 0.9rem;
+}
+
+.wiki-hot-tag {
+    display: inline-block;
+    padding: 0.3rem 0.75rem;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 20px;
+    color: var(--tblr-secondary);
+    font-size: 0.8125rem;
+    text-decoration: none;
+    margin: 2px;
+    transition: background 0.12s, color 0.12s;
+}
+
+.wiki-hot-tag:hover {
+    background: var(--wp-brand-light);
+    border-color: var(--wp-brand);
+    color: var(--wp-brand-dark);
+}
+
+/* ── 语言选择 ── */
+.wiki-home-languages {
+    width: 100%;
+    max-width: 800px;
+    margin: 0 auto 2rem;
+}
+
+.wiki-home-divider {
+    display: flex;
+    align-items: center;
+    text-align: center;
+    margin-bottom: 1.5rem;
+    gap: 1.5rem;
+    color: var(--tblr-secondary);
+    font-size: 0.875rem;
+}
+
+.wiki-home-divider::before,
+.wiki-home-divider::after {
+    content: "";
+    flex: 1;
+    border-bottom: 1px solid var(--tblr-border-color);
+}
+
+.wiki-language-tags {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+    gap: 0.75rem;
+}
+
+.wiki-language-tag {
+    display: inline-block;
+    padding: 0.4rem 1.125rem;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 30px;
+    color: var(--tblr-body-color);
+    font-size: 0.875rem;
+    text-decoration: none;
+    transition: background 0.12s, border-color 0.12s;
+}
+
+.wiki-language-tag:hover {
+    background: var(--wp-brand-light);
+    border-color: var(--wp-brand);
+    color: var(--wp-brand-dark);
+}
+
+.wiki-language-tag.active {
+    background: var(--wp-brand);
+    border-color: var(--wp-brand);
+    color: #fff;
+}
+
+.wiki-language-tag.active:hover {
+    background: var(--wp-brand-dark);
+    border-color: var(--wp-brand-dark);
+}
+
+/* ── 统计 ── */
+.wiki-home-stats {
+    text-align: center;
+    font-size: 0.875rem;
+    padding-top: 1.5rem;
+    border-top: 1px solid var(--tblr-border-color);
+    width: 100%;
+    max-width: 640px;
+}
+
+/* ── 响应式 ── */
+@media (max-width: 768px) {
+    .wiki-home-container {
+        min-height: calc(100vh - 200px);
+        padding: 2rem 1rem;
+    }
+
+    .wiki-home-wheel-img {
+        width: 90px;
+    }
+
+    .wiki-home-title h1 {
+        font-size: 1.75rem;
+    }
+
+    .wiki-language-tag {
+        padding: 0.35rem 0.875rem;
+        font-size: 0.8125rem;
+    }
+}

+ 163 - 0
api-v12/resources/css/modules/_wiki_home_append.css

@@ -0,0 +1,163 @@
+/* ══════════════════════════════════════════
+   十五、Wiki 首页(home.blade.php)
+   替代原 home.blade.php 内联 <style>,风格与 wiki 整体保持一致
+   ══════════════════════════════════════════ */
+
+.wiki-home-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    min-height: calc(100vh - 300px);
+    padding: 3rem 1.5rem;
+}
+
+/* ── 法轮图标 ── */
+.wiki-home-wheel {
+    margin-bottom: 1.5rem;
+}
+
+.wiki-home-wheel-img {
+    width: 120px;
+    height: auto;
+    opacity: 0.85;
+    transition: transform 0.3s ease;
+}
+
+.wiki-home-wheel-img:hover {
+    transform: scale(1.05);
+}
+
+/* ── 标题 ── */
+.wiki-home-title {
+    text-align: center;
+    margin-bottom: 2rem;
+}
+
+.wiki-home-title h1 {
+    font-family: "Noto Serif", Georgia, serif;
+    font-size: 2.25rem;
+    font-weight: 600;
+    color: var(--tblr-body-color);
+    margin-bottom: 0.5rem;
+}
+
+/* ── 搜索框 ── */
+.wiki-home-search {
+    width: 100%;
+    max-width: 640px;
+    margin: 0 auto 1.5rem;
+}
+
+/* ── 热门标签 ── */
+.wiki-home-hot-tags {
+    text-align: center;
+    margin-bottom: 2.5rem;
+    font-size: 0.9rem;
+}
+
+.wiki-hot-tag {
+    display: inline-block;
+    padding: 0.3rem 0.75rem;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 20px;
+    color: var(--tblr-secondary);
+    font-size: 0.8125rem;
+    text-decoration: none;
+    margin: 2px;
+    transition: background 0.12s, color 0.12s;
+}
+
+.wiki-hot-tag:hover {
+    background: var(--wp-brand-light);
+    border-color: var(--wp-brand);
+    color: var(--wp-brand-dark);
+}
+
+/* ── 语言选择 ── */
+.wiki-home-languages {
+    width: 100%;
+    max-width: 800px;
+    margin: 0 auto 2rem;
+}
+
+.wiki-home-divider {
+    display: flex;
+    align-items: center;
+    text-align: center;
+    margin-bottom: 1.5rem;
+    gap: 1.5rem;
+    color: var(--tblr-secondary);
+    font-size: 0.875rem;
+}
+
+.wiki-home-divider::before,
+.wiki-home-divider::after {
+    content: '';
+    flex: 1;
+    border-bottom: 1px solid var(--tblr-border-color);
+}
+
+.wiki-language-tags {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+    gap: 0.75rem;
+}
+
+.wiki-language-tag {
+    display: inline-block;
+    padding: 0.4rem 1.125rem;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 30px;
+    color: var(--tblr-body-color);
+    font-size: 0.875rem;
+    text-decoration: none;
+    transition: background 0.12s, border-color 0.12s;
+}
+
+.wiki-language-tag:hover {
+    background: var(--wp-brand-light);
+    border-color: var(--wp-brand);
+    color: var(--wp-brand-dark);
+}
+
+.wiki-language-tag.active {
+    background: var(--wp-brand);
+    border-color: var(--wp-brand);
+    color: #fff;
+}
+
+.wiki-language-tag.active:hover {
+    background: var(--wp-brand-dark);
+    border-color: var(--wp-brand-dark);
+}
+
+/* ── 统计 ── */
+.wiki-home-stats {
+    text-align: center;
+    font-size: 0.875rem;
+    padding-top: 1.5rem;
+    border-top: 1px solid var(--tblr-border-color);
+    width: 100%;
+    max-width: 640px;
+}
+
+/* ── 响应式 ── */
+@media (max-width: 768px) {
+    .wiki-home-container {
+        min-height: calc(100vh - 200px);
+        padding: 2rem 1rem;
+    }
+
+    .wiki-home-wheel-img { width: 90px; }
+
+    .wiki-home-title h1  { font-size: 1.75rem; }
+
+    .wiki-language-tag {
+        padding: 0.35rem 0.875rem;
+        font-size: 0.8125rem;
+    }
+}

+ 5 - 3
api-v12/resources/js/app.js

@@ -1,5 +1,7 @@
 // resources/js/app.js
 // resources/js/app.js
-import "./bootstrap";
-import "./term-tooltip";
+import './bootstrap';
+import { initNavbar } from './modules/navbar';
 
 
-import "@tabler/core/dist/js/tabler.min.js";
+document.addEventListener('DOMContentLoaded', () => {
+    initNavbar();
+});

+ 30 - 0
api-v12/resources/js/modules/navbar.js

@@ -0,0 +1,30 @@
+// resources/js/modules/navbar.js
+// 顶部导航 mobile 抽屉开关逻辑。
+// 来源:library/layouts/app.blade.php 内联 script(去除了无效的 mobileMenu 引用)。
+
+export function initNavbar() {
+    const btn     = document.getElementById('bcHamburger');
+    const overlay = document.getElementById('bcOverlay');
+    const drawer  = document.getElementById('bcDrawer');
+    const close   = document.getElementById('bcDrawerClose');
+
+    if (!btn) return;
+
+    function open() {
+        drawer.classList.add('open');
+        overlay.classList.add('open');
+    }
+
+    function shut() {
+        drawer.classList.remove('open');
+        overlay.classList.remove('open');
+    }
+
+    btn.addEventListener('click', open);
+    overlay.addEventListener('click', shut);
+    close.addEventListener('click', shut);
+
+    drawer.querySelectorAll('a').forEach(a => {
+        a.addEventListener('click', shut);
+    });
+}

+ 233 - 0
api-v12/resources/js/modules/term-tooltip.js

@@ -0,0 +1,233 @@
+// resources/js/modules/term-tooltip.js
+// 原 term-tooltip.js 移至此处(从 js/ 根目录移入 modules/)。
+// 所有阅读页公用(tipitaka read、anthology read、wiki show、blog show)。
+// 在 reader.js 中 import,不在 app.js 全局加载(仅阅读页需要)。
+//
+// 文件内容:将现有 term-tooltip.js 内容直接复制至此,不做修改。
+// 待 reader.js 建立后在此 import:
+//   import './modules/term-tooltip';
+
+import * as bootstrap from "bootstrap";
+
+(function () {
+    "use strict";
+
+    // ── 缓存层 ────────────────────────────────────────────────────────
+    const cache = {};
+
+    async function fetchTerm(id) {
+        if (cache[id]) return cache[id];
+        const res = await fetch(`/api/v2/terms/${id}`);
+        if (!res.ok) throw new Error(`fetchTerm ${id} failed: ${res.status}`);
+        const json = await res.json();
+        cache[id] = json.data;
+        return json.data;
+    }
+
+    // ── 设备判断 ──────────────────────────────────────────────────────
+    const isMobile = () => window.innerWidth < 768;
+
+    // ── Skeleton 模板 ─────────────────────────────────────────────────
+    function buildSkeletonContent() {
+        return `
+            <div class="wiki-term-skeleton">
+                <div class="wiki-term-skeleton-word"></div>
+                <div class="wiki-term-skeleton-line"></div>
+                <div class="wiki-term-skeleton-line short"></div>
+            </div>
+        `;
+    }
+
+    // ── Popover 内容模板 ──────────────────────────────────────────────
+    function buildPopoverContent(data) {
+        const meaning = (data.meaning || "").trim();
+        const summary = (data.summary || "").trim();
+        const showSummary = summary && summary !== meaning;
+
+        return `
+            <div class="wiki-term-card-word">${data.word || ""}</div>
+            <div class="wiki-term-card-body" style="padding: 10px 14px 12px;">
+                ${
+                    meaning
+                        ? `<div class="wiki-term-card-meaning">${meaning}</div>`
+                        : ""
+                }
+                ${
+                    showSummary
+                        ? `<div class="wiki-term-card-summary">${summary}</div>`
+                        : ""
+                }
+            </div>
+            <div><a>查看完整条目</a></div>
+        `;
+    }
+
+    // ── 桌面端:Bootstrap Popover ─────────────────────────────────────
+    function initDesktopPopover(el, content) {
+        if (el._wikiPopover) return el._wikiPopover;
+
+        const popover = new bootstrap.Popover(el, {
+            trigger: "manual",
+            html: true,
+            placement: "bottom",
+            fallbackPlacements: ["top", "bottom"],
+            customClass: "wiki-term-popover",
+            content: content,
+            sanitize: false,
+        });
+
+        el._wikiPopover = popover;
+
+        let hideTimer = null;
+
+        function scheduleHide() {
+            hideTimer = setTimeout(() => {
+                const tipEl = document.querySelector(".wiki-term-popover.show");
+                if (tipEl && tipEl.matches(":hover")) return;
+                popover.hide();
+            }, 120);
+        }
+
+        function cancelHide() {
+            clearTimeout(hideTimer);
+        }
+
+        el.addEventListener("mouseenter", () => {
+            cancelHide();
+            popover.show();
+        });
+
+        el.addEventListener("mouseleave", scheduleHide);
+
+        el.addEventListener("shown.bs.popover", () => {
+            const tipEl = document.querySelector(".wiki-term-popover.show");
+            if (!tipEl) return;
+            tipEl.addEventListener("mouseenter", cancelHide);
+            tipEl.addEventListener("mouseleave", scheduleHide);
+        });
+
+        return popover;
+    }
+
+    function updateDesktopPopover(el, data) {
+        const popover = el._wikiPopover;
+        if (!popover) return;
+        // 更新内容
+        const tip = document.querySelector(".wiki-term-popover.show");
+        if (tip) {
+            tip.querySelector(".popover-body").innerHTML =
+                buildPopoverContent(data);
+        }
+        // 同步 popover 内部 config,供下次 show 使用
+        popover._config.content = buildPopoverContent(data);
+    }
+
+    // ── 移动端:Bootstrap Offcanvas ───────────────────────────────────
+    let offcanvasInstance = null;
+
+    function getOffcanvas() {
+        if (offcanvasInstance) return offcanvasInstance;
+        const el = document.getElementById("wikiTermDrawer");
+        if (!el) return null;
+        offcanvasInstance = new bootstrap.Offcanvas(el, { scroll: false });
+
+        el.addEventListener("hidden.bs.offcanvas", () => {
+            document
+                .querySelectorAll(".offcanvas-backdrop")
+                .forEach((b) => b.remove());
+            document.body.classList.remove("modal-open");
+            document.body.style.removeProperty("overflow");
+            document.body.style.removeProperty("padding-right");
+        });
+
+        return offcanvasInstance;
+    }
+
+    function showMobileDrawerSkeleton() {
+        const oc = getOffcanvas();
+        if (!oc) return;
+
+        document.getElementById("wikiTermDrawerWord").innerHTML =
+            '<div class="wiki-term-skeleton-word"></div>';
+        document.getElementById("wikiTermDrawerMeaning").innerHTML =
+            '<div class="wiki-term-skeleton-line short"></div>';
+        document.getElementById(
+            "wikiTermCardSlot"
+        ).innerHTML = `<div class="wiki-term-skeleton">
+                <div class="wiki-term-skeleton-line"></div>
+                <div class="wiki-term-skeleton-line short"></div>
+            </div>`;
+        document.getElementById("wikiTermDrawerLink").style.display = "none";
+
+        oc.show();
+    }
+
+    function fillMobileDrawer(data) {
+        const meaning = (data.meaning || "").trim();
+        const summary = (data.summary || "").trim();
+        const showSummary = summary && summary !== meaning;
+
+        document.getElementById("wikiTermDrawerWord").textContent =
+            data.word || "";
+        document.getElementById("wikiTermDrawerMeaning").textContent = meaning;
+        document.getElementById("wikiTermCardSlot").innerHTML = showSummary
+            ? `<div class="wiki-term-card-summary">${summary}</div>`
+            : "";
+    }
+
+    // ── 入口:扫描所有 .term-ref ──────────────────────────────────────
+    function init() {
+        const refs = document.querySelectorAll(".term-ref[data-id]");
+        if (!refs.length) return;
+
+        refs.forEach((el) => {
+            // 桌面:mouseenter 时先显示 skeleton,数据回来后更新
+            el.addEventListener("mouseenter", async function onFirstEnter() {
+                if (isMobile()) return;
+
+                // 立即显示 skeleton popover
+                const popover = initDesktopPopover(el, buildSkeletonContent());
+                popover.show();
+
+                try {
+                    const data = await fetchTerm(el.dataset.id);
+                    updateDesktopPopover(el, data);
+                } catch (e) {
+                    console.warn(
+                        "[WikiPāli] term fetch failed",
+                        el.dataset.id,
+                        e
+                    );
+                    popover.hide();
+                }
+
+                el.removeEventListener("mouseenter", onFirstEnter);
+            });
+
+            // 移动端:点击立即弹出 skeleton,数据回来后填充
+            el.addEventListener("click", async (e) => {
+                if (!isMobile()) return;
+                e.preventDefault();
+
+                showMobileDrawerSkeleton();
+
+                try {
+                    const data = await fetchTerm(el.dataset.id);
+                    fillMobileDrawer(data);
+                } catch (err) {
+                    console.warn(
+                        "[WikiPāli] term fetch failed",
+                        el.dataset.id,
+                        err
+                    );
+                }
+            });
+        });
+    }
+
+    if (document.readyState === "loading") {
+        document.addEventListener("DOMContentLoaded", init);
+    } else {
+        init();
+    }
+})();

+ 24 - 0
api-v12/resources/views/components/library/footer.blade.php

@@ -0,0 +1,24 @@
+{{-- resources/views/components/library/footer.blade.php
+     Library 栏目全站 footer。
+     当前为最简占位版本,后续按需扩展。
+--}}
+<footer class="footer footer-transparent d-print-none">
+    <div class="container-xl">
+        <div class="row text-center align-items-center">
+            <div class="col-12 col-lg-auto mt-3 mt-lg-0">
+                <ul class="list-inline list-inline-dots mb-0">
+                    <li class="list-inline-item">
+                        <a href="{{ route('library.home') }}" class="link-secondary">
+                            WikiPāli
+                        </a>
+                    </li>
+                    <li class="list-inline-item">
+                        <a href="{{ route('library.download') }}" class="link-secondary">
+                            下载
+                        </a>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </div>
+</footer>

+ 62 - 0
api-v12/resources/views/components/library/navbar.blade.php

@@ -0,0 +1,62 @@
+{{-- resources/views/components/library/navbar.blade.php
+     替换原 components/library/header.blade.php。
+     文件名从 header 改为 navbar,与规范目录对齐。
+     原文件内容不变,仅做变量名规范化(--wp-* token 替代硬编码色值已在 CSS 层处理)。
+--}}
+<div class="anthology-breadcrumb-bar">
+    <div class="container-xl">
+        <div class="bc-inner">
+
+            <ol class="breadcrumb mb-0">
+                @yield('breadcrumb')
+            </ol>
+
+            <ul class="bc-nav">
+                <li><a href="{{ route('library.home') }}"
+                       class="{{ request()->routeIs('library.home') ? 'active' : '' }}">首页</a></li>
+                <li><a href="{{ route('library.tipitaka.index') }}"
+                       class="{{ request()->routeIs('library.tipitaka.*') ? 'active' : '' }}">三藏</a></li>
+                <li><a href="{{ route('library.wiki.home') }}"
+                       class="{{ request()->routeIs('library.wiki.*') ? 'active' : '' }}">百科</a></li>
+                <li><a href="{{ route('library.anthology.index') }}"
+                       class="{{ request()->routeIs('library.anthology.*') ? 'active' : '' }}">文集</a></li>
+                <li><a href="{{ route('library.download') }}"
+                       class="{{ request()->routeIs('library.download') ? 'active' : '' }}">下载</a></li>
+                <li>
+                    <x-language-switcher />
+                </li>
+            </ul>
+
+            <button class="bc-hamburger" id="bcHamburger" aria-label="打开导航">
+                <i class="ti ti-menu-2"></i>
+            </button>
+
+        </div>
+    </div>
+</div>
+
+<div class="bc-mobile-overlay" id="bcOverlay"></div>
+
+<div class="bc-mobile-drawer" id="bcDrawer">
+    <div class="bc-mobile-drawer-header">
+        <span>导航</span>
+        <button class="bc-mobile-drawer-close" id="bcDrawerClose" aria-label="关闭">
+            <i class="ti ti-x"></i>
+        </button>
+    </div>
+    <ul class="bc-mobile-nav">
+        <li><a href="{{ route('library.home') }}"
+               class="{{ request()->routeIs('library.home') ? 'active' : '' }}">首页</a></li>
+        <li><a href="{{ route('library.tipitaka.index') }}"
+               class="{{ request()->routeIs('library.tipitaka.*') ? 'active' : '' }}">三藏</a></li>
+        <li><a href="{{ route('library.wiki.home') }}"
+               class="{{ request()->routeIs('library.wiki.*') ? 'active' : '' }}">百科</a></li>
+        <li><a href="{{ route('library.anthology.index') }}"
+               class="{{ request()->routeIs('library.anthology.*') ? 'active' : '' }}">文集</a></li>
+        <li><a href="{{ route('library.download') }}"
+               class="{{ request()->routeIs('library.download') ? 'active' : '' }}">下载</a></li>
+        <li style="padding: 1rem 0.25rem;">
+            <x-language-switcher />
+        </li>
+    </ul>
+</div>

+ 35 - 0
api-v12/resources/views/components/ui/empty-state.blade.php

@@ -0,0 +1,35 @@
+{{-- resources/views/components/ui/empty-state.blade.php
+     通用空状态组件。
+     Props:
+       $title — 标题文字
+       $desc  — 描述文字(支持 HTML,可选)
+       $icon  — 自定义图标 slot(可选,默认搜索图标)
+--}}
+@props([
+    'title' => '未找到相关内容',
+    'desc'  => '',
+])
+
+<div class="wiki-empty-state">
+    <div class="wiki-empty-icon">
+        {{ $icon ?? '' }}
+        @unless($icon ?? false)
+        <svg width="32" height="32" viewBox="0 0 24 24" fill="none"
+             stroke="currentColor" stroke-width="1.5">
+            <circle cx="11" cy="11" r="8" />
+            <path d="M21 21l-4.35-4.35" stroke-linecap="round" />
+            <path d="M8 11h6M11 8v6" stroke-linecap="round" />
+        </svg>
+        @endunless
+    </div>
+
+    <div class="wiki-empty-title">{{ $title }}</div>
+
+    @if ($desc)
+    <div class="wiki-empty-desc">{!! $desc !!}</div>
+    @endif
+
+    @isset($slot)
+        {{ $slot }}
+    @endisset
+</div>

+ 42 - 0
api-v12/resources/views/components/ui/search-input.blade.php

@@ -0,0 +1,42 @@
+{{-- resources/views/components/ui/search-input.blade.php
+     通用搜索输入框组件。
+     Props:
+       $action      — 表单提交路由
+       $value       — 当前搜索词(默认空)
+       $placeholder — 占位文字
+       $buttonText  — 按钮文字(默认"搜索")
+       $size        — lg | md(默认 md)
+       $hiddenFields — 额外隐藏字段 array ['name' => 'value']
+       $autofocus   — 是否自动聚焦(默认 false)
+--}}
+@props([
+    'action',
+    'value'        => '',
+    'placeholder'  => '搜索…',
+    'buttonText'   => '搜索',
+    'size'         => 'md',
+    'hiddenFields' => [],
+    'autofocus'    => false,
+])
+
+<form action="{{ $action }}" method="GET" class="search-input-form">
+    <div class="input-group {{ $size === 'lg' ? 'input-group-lg' : '' }}">
+        <input
+            type="text"
+            name="q"
+            class="form-control search-input"
+            value="{{ $value }}"
+            placeholder="{{ $placeholder }}"
+            {{ $autofocus ? 'autofocus' : '' }} />
+
+        @foreach ($hiddenFields as $name => $val)
+            @if ($val)
+                <input type="hidden" name="{{ $name }}" value="{{ $val }}">
+            @endif
+        @endforeach
+
+        <button class="btn btn-primary" type="submit">
+            {{ $buttonText }}
+        </button>
+    </div>
+</form>

+ 26 - 0
api-v12/resources/views/layouts/base.blade.php

@@ -0,0 +1,26 @@
+{{-- resources/views/layouts/base.blade.php
+     全站 HTML 骨架。不含任何导航、页脚、布局结构。
+     所有子布局继承此文件。
+--}}
+<!doctype html>
+<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
+
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
+    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
+    <title>@yield('title', 'WikiPāli · 巴利佛典百科')</title>
+
+    {{-- 基础样式由子布局通过 @vite 注入 --}}
+    @stack('styles')
+</head>
+
+<body class="@yield('body-class')">
+    <div class="page">
+        @yield('page')
+    </div>
+
+    @stack('scripts')
+</body>
+
+</html>

+ 37 - 0
api-v12/resources/views/layouts/library.blade.php

@@ -0,0 +1,37 @@
+{{-- resources/views/layouts/library.blade.php
+     library/* 所有列表页、详情页的布局外壳。
+     包含:navbar(header 组件)、可选 hero、可选 toolbar、主内容区、footer。
+     阅读页使用 layouts/reader,不继承此文件。
+--}}
+@extends('layouts.base')
+
+@push('styles')
+@vite(['resources/css/library.css', 'resources/js/app.js'])
+@endpush
+
+@section('page')
+
+{{-- 导航 + 可选 Hero 包裹层(Hero 存在时 breadcrumb bar 绝对定位覆盖其上) --}}
+<div class="hero-wrapper">
+    <x-library.navbar />
+    @yield('hero')
+</div>
+
+{{-- 可选工具条 --}}
+@hasSection('toolbar')
+<div class="page-toolbar">
+    <div class="container-xl">
+        @yield('toolbar')
+    </div>
+</div>
+@endif
+
+{{-- 主内容区 --}}
+<div class="page-wrapper">
+    @yield('content')
+</div>
+
+{{-- Footer --}}
+<x-library.footer />
+
+@endsection

+ 423 - 251
api-v12/resources/views/library/anthology/index.blade.php

@@ -1,3 +1,4 @@
+{{-- api-v12/resources/views/library/anthology/index.blade.php --}}
 @extends('library.layouts.app')
 @extends('library.layouts.app')
 
 
 @section('title', '文集 · 巴利书库')
 @section('title', '文集 · 巴利书库')
@@ -6,245 +7,416 @@
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
 <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
 <style>
 <style>
-:root {
-    --sf: #c8860a;
-    --sf-light: #f5e6c8;
-    --sf-pale: #fdf8f0;
-    --ink: #1a1208;
-    --ink-soft: #4a3f2f;
-    --ink-muted: #8a7a68;
-    --bdr: #e8ddd0;
-    --card-bg: #fffdf9;
-}
-body { background: var(--sf-pale) !important; font-family: 'Noto Sans SC', sans-serif; }
-
-/* Page header */
-.anthology-page-header {
-    background: linear-gradient(135deg, var(--ink) 0%, #2d2010 100%);
-    padding: 2.25rem 0 2rem;
-    position: relative;
-    overflow: hidden;
-    margin-bottom: 0;
-}
-.anthology-page-header::before {
-    content: '藏';
-    font-family: 'Noto Serif SC', serif;
-    font-size: 16rem;
-    font-weight: 700;
-    color: rgba(255,255,255,0.03);
-    position: absolute;
-    right: -1rem;
-    top: -2.5rem;
-    line-height: 1;
-    pointer-events: none;
-}
-.anthology-page-header h1 {
-    font-family: 'Noto Serif SC', serif;
-    font-size: 1.75rem;
-    font-weight: 600;
-    color: #fff;
-    margin: 0 0 0.3rem;
-    letter-spacing: 0.08em;
-}
-.anthology-page-header p { color: rgba(255,255,255,0.45); font-size: 0.85rem; margin: 0; }
-.result-badge {
-    background: var(--sf);
-    color: var(--ink);
-    font-size: 0.75rem;
-    font-weight: 700;
-    padding: 2px 9px;
-    border-radius: 20px;
-    margin-left: 0.6rem;
-    vertical-align: middle;
-}
-
-/* Card */
-.anthology-card {
-    background: var(--card-bg);
-    border: 1px solid var(--bdr);
-    border-radius: 10px;
-    overflow: hidden;
-    display: flex;
-    transition: box-shadow .25s, transform .25s;
-    margin-bottom: 1.1rem;
-    text-decoration: none;
-    color: inherit;
-}
-.anthology-card:hover {
-    box-shadow: 0 8px 28px rgba(200,134,10,.12), 0 2px 8px rgba(0,0,0,.06);
-    transform: translateY(-2px);
-    color: inherit;
-    text-decoration: none;
-}
-.card-cover {
-    width: 130px;
-    min-width: 130px;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    padding: 1.1rem 0.7rem;
-    position: relative;
-    overflow: hidden;
-}
-.card-cover img {
-    width: 100%;
-    height: 100%;
-    object-fit: cover;
-    position: absolute;
-    inset: 0;
-}
-.card-cover::after {
-    content: '';
-    position: absolute;
-    inset: 0;
-    background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(255,255,255,.015) 8px, rgba(255,255,255,.015) 9px);
-}
-.cover-text-wrap { position: relative; z-index: 1; text-align: center; }
-.cover-title {
-    font-family: 'Noto Serif SC', serif;
-    font-size: 1rem;
-    font-weight: 600;
-    color: #fff;
-    line-height: 1.6;
-    letter-spacing: .12em;
-    word-break: break-all;
-}
-.cover-divider { width: 28px; height: 1px; background: var(--sf); margin: .5rem auto; }
-.cover-sub { font-size: .65rem; color: rgba(255,255,255,.45); letter-spacing: .04em; }
-
-.card-body-inner { padding: 1.1rem 1.4rem; flex: 1; display: flex; flex-direction: column; }
-.card-main-title {
-    font-family: 'Noto Serif SC', serif;
-    font-size: 1.1rem;
-    font-weight: 600;
-    color: var(--ink);
-    margin-bottom: .35rem;
-    line-height: 1.4;
-}
-.anthology-card:hover .card-main-title { color: var(--sf); }
-.card-desc { font-size: .8rem; color: var(--ink-muted); margin-bottom: .65rem; line-height: 1.65; }
-.card-author { display: flex; align-items: center; gap: .45rem; margin-bottom: .7rem; }
-.a-avatar {
-    width: 24px; height: 24px; border-radius: 50%;
-    display: flex; align-items: center; justify-content: center;
-    font-size: .65rem; font-weight: 700; color: #fff; flex-shrink: 0;
-}
-.a-avatar-img {
-    width: 24px; height: 24px; border-radius: 50%;
-    object-fit: cover; flex-shrink: 0;
-}
-.a-name { font-size: .8rem; color: var(--ink-soft); font-weight: 500; }
-.chapter-tags { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: auto; }
-.c-tag {
-    font-size: .7rem; color: var(--ink-muted);
-    background: var(--sf-light); border: 1px solid var(--bdr);
-    padding: 1px 7px; border-radius: 4px; white-space: nowrap;
-}
-.c-tag.more { background: transparent; border-color: transparent; color: var(--sf); }
-.card-meta-row {
-    display: flex; align-items: center; gap: .85rem;
-    margin-top: .65rem; padding-top: .65rem;
-    border-top: 1px solid var(--bdr);
-}
-.meta-it { font-size: .72rem; color: var(--ink-muted); display: flex; align-items: center; gap: .25rem; }
-
-/* Pagination — force horizontal */
-.anthology-pagination .pagination {
-    display: flex !important;
-    flex-direction: row !important;
-    flex-wrap: wrap;
-    gap: 3px;
-    justify-content: center;
-    list-style: none;
-    padding: 0;
-    margin: 0;
-}
-.anthology-pagination .page-item { display: inline-block; }
-.anthology-pagination .page-item .page-link {
-    border: 1px solid var(--bdr);
-    color: var(--ink-soft);
-    font-size: .82rem;
-    padding: 5px 11px;
-    background: var(--card-bg);
-    border-radius: 5px;
-    display: inline-block;
-    line-height: 1.5;
-    text-decoration: none;
-}
-.anthology-pagination .page-item.active .page-link { background: var(--sf); border-color: var(--sf); color: #fff; }
-.anthology-pagination .page-item .page-link:hover { background: var(--sf-light); color: var(--sf); }
-.anthology-pagination .page-item.disabled .page-link { opacity: .45; pointer-events: none; }
-
-/* Sidebar */
-.sidebar-box {
-    background: var(--card-bg);
-    border: 1px solid var(--bdr);
-    border-radius: 10px;
-    overflow: hidden;
-    margin-bottom: 1.1rem;
-}
-.sb-header {
-    padding: .75rem 1.15rem;
-    border-bottom: 1px solid var(--bdr);
-    font-family: 'Noto Serif SC', serif;
-    font-size: .875rem;
-    font-weight: 600;
-    color: var(--ink-soft);
-    letter-spacing: .04em;
-    display: flex;
-    align-items: center;
-    gap: .45rem;
-}
-.sb-header::before {
-    content: '';
-    display: block;
-    width: 3px;
-    height: 13px;
-    background: var(--sf);
-    border-radius: 2px;
-}
-.author-ul { list-style: none; padding: .35rem 0; margin: 0; }
-.author-ul li a {
-    display: flex; align-items: center; gap: .6rem;
-    padding: .45rem 1.15rem;
-    text-decoration: none;
-    transition: background .15s;
-}
-.author-ul li a:hover { background: var(--sf-pale); }
-.au-avatar {
-    width: 28px; height: 28px; border-radius: 50%;
-    display: flex; align-items: center; justify-content: center;
-    font-size: .68rem; font-weight: 700; color: #fff; flex-shrink: 0;
-}
-.au-avatar-img {
-    width: 28px; height: 28px; border-radius: 50%;
-    object-fit: cover; flex-shrink: 0;
-}
-.au-name { font-size: .8rem; color: var(--ink-soft); font-weight: 500; display: block; }
-.au-count { font-size: .7rem; color: var(--ink-muted); }
-
-/* Search */
-.search-wrap { background: var(--card-bg); border: 1px solid var(--bdr); border-radius: 10px; padding: .85rem 1.1rem; margin-bottom: 1.1rem; }
-.search-input {
-    width: 100%;
-    border: 1px solid var(--bdr);
-    border-radius: 6px;
-    padding: .45rem .9rem;
-    font-size: .83rem;
-    font-family: 'Noto Sans SC', sans-serif;
-    background: var(--sf-pale);
-    color: var(--ink);
-    outline: none;
-    transition: border-color .2s;
-}
-.search-input:focus { border-color: var(--sf); background: #fff; }
-.search-input::placeholder { color: var(--ink-muted); }
-
-@media (max-width:768px) {
-    .anthology-card { flex-direction: column; }
-    .card-cover { width: 100%; min-width: unset; height: 90px; }
-}
+    :root {
+        --sf: #c8860a;
+        --sf-light: #f5e6c8;
+        --sf-pale: #fdf8f0;
+        --ink: #1a1208;
+        --ink-soft: #4a3f2f;
+        --ink-muted: #8a7a68;
+        --bdr: #e8ddd0;
+        --card-bg: #fffdf9;
+    }
+
+    body {
+        background: var(--sf-pale) !important;
+        font-family: 'Noto Sans SC', sans-serif;
+    }
+
+    /* Page header */
+    .anthology-page-header {
+        background: linear-gradient(135deg, var(--ink) 0%, #2d2010 100%);
+        padding: 2.25rem 0 2rem;
+        position: relative;
+        overflow: hidden;
+        margin-bottom: 0;
+    }
+
+    .anthology-page-header::before {
+        content: '藏';
+        font-family: 'Noto Serif SC', serif;
+        font-size: 16rem;
+        font-weight: 700;
+        color: rgba(255, 255, 255, 0.03);
+        position: absolute;
+        right: -1rem;
+        top: -2.5rem;
+        line-height: 1;
+        pointer-events: none;
+    }
+
+    .anthology-page-header h1 {
+        font-family: 'Noto Serif SC', serif;
+        font-size: 1.75rem;
+        font-weight: 600;
+        color: #fff;
+        margin: 0 0 0.3rem;
+        letter-spacing: 0.08em;
+    }
+
+    .anthology-page-header p {
+        color: rgba(255, 255, 255, 0.45);
+        font-size: 0.85rem;
+        margin: 0;
+    }
+
+    .result-badge {
+        background: var(--sf);
+        color: var(--ink);
+        font-size: 0.75rem;
+        font-weight: 700;
+        padding: 2px 9px;
+        border-radius: 20px;
+        margin-left: 0.6rem;
+        vertical-align: middle;
+    }
+
+    /* Card */
+    .anthology-card {
+        background: var(--card-bg);
+        border: 1px solid var(--bdr);
+        border-radius: 10px;
+        overflow: hidden;
+        display: flex;
+        transition: box-shadow .25s, transform .25s;
+        margin-bottom: 1.1rem;
+        text-decoration: none;
+        color: inherit;
+    }
+
+    .anthology-card:hover {
+        box-shadow: 0 8px 28px rgba(200, 134, 10, .12), 0 2px 8px rgba(0, 0, 0, .06);
+        transform: translateY(-2px);
+        color: inherit;
+        text-decoration: none;
+    }
+
+    .card-cover {
+        width: 130px;
+        min-width: 130px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        padding: 1.1rem 0.7rem;
+        position: relative;
+        overflow: hidden;
+    }
+
+    .card-cover img {
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+        position: absolute;
+        inset: 0;
+    }
+
+    .card-cover::after {
+        content: '';
+        position: absolute;
+        inset: 0;
+        background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(255, 255, 255, .015) 8px, rgba(255, 255, 255, .015) 9px);
+    }
+
+    .cover-text-wrap {
+        position: relative;
+        z-index: 1;
+        text-align: center;
+    }
+
+    .cover-title {
+        font-family: 'Noto Serif SC', serif;
+        font-size: 1rem;
+        font-weight: 600;
+        color: #fff;
+        line-height: 1.6;
+        letter-spacing: .12em;
+        word-break: break-all;
+    }
+
+    .cover-divider {
+        width: 28px;
+        height: 1px;
+        background: var(--sf);
+        margin: .5rem auto;
+    }
+
+    .cover-sub {
+        font-size: .65rem;
+        color: rgba(255, 255, 255, .45);
+        letter-spacing: .04em;
+    }
+
+    .card-body-inner {
+        padding: 1.1rem 1.4rem;
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+    }
+
+    .card-main-title {
+        font-family: 'Noto Serif SC', serif;
+        font-size: 1.1rem;
+        font-weight: 600;
+        color: var(--ink);
+        margin-bottom: .35rem;
+        line-height: 1.4;
+    }
+
+    .anthology-card:hover .card-main-title {
+        color: var(--sf);
+    }
+
+    .card-desc {
+        font-size: .8rem;
+        color: var(--ink-muted);
+        margin-bottom: .65rem;
+        line-height: 1.65;
+    }
+
+    .card-author {
+        display: flex;
+        align-items: center;
+        gap: .45rem;
+        margin-bottom: .7rem;
+    }
+
+    .a-avatar {
+        width: 24px;
+        height: 24px;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: .65rem;
+        font-weight: 700;
+        color: #fff;
+        flex-shrink: 0;
+    }
+
+    .a-avatar-img {
+        width: 24px;
+        height: 24px;
+        border-radius: 50%;
+        object-fit: cover;
+        flex-shrink: 0;
+    }
+
+    .a-name {
+        font-size: .8rem;
+        color: var(--ink-soft);
+        font-weight: 500;
+    }
+
+    .chapter-tags {
+        display: flex;
+        flex-wrap: wrap;
+        gap: .3rem;
+        margin-top: auto;
+    }
+
+    .c-tag {
+        font-size: .7rem;
+        color: var(--ink-muted);
+        background: var(--sf-light);
+        border: 1px solid var(--bdr);
+        padding: 1px 7px;
+        border-radius: 4px;
+        white-space: nowrap;
+    }
+
+    .c-tag.more {
+        background: transparent;
+        border-color: transparent;
+        color: var(--sf);
+    }
+
+    .card-meta-row {
+        display: flex;
+        align-items: center;
+        gap: .85rem;
+        margin-top: .65rem;
+        padding-top: .65rem;
+        border-top: 1px solid var(--bdr);
+    }
+
+    .meta-it {
+        font-size: .72rem;
+        color: var(--ink-muted);
+        display: flex;
+        align-items: center;
+        gap: .25rem;
+    }
+
+    /* Pagination — force horizontal */
+    .anthology-pagination .pagination {
+        display: flex !important;
+        flex-direction: row !important;
+        flex-wrap: wrap;
+        gap: 3px;
+        justify-content: center;
+        list-style: none;
+        padding: 0;
+        margin: 0;
+    }
+
+    .anthology-pagination .page-item {
+        display: inline-block;
+    }
+
+    .anthology-pagination .page-item .page-link {
+        border: 1px solid var(--bdr);
+        color: var(--ink-soft);
+        font-size: .82rem;
+        padding: 5px 11px;
+        background: var(--card-bg);
+        border-radius: 5px;
+        display: inline-block;
+        line-height: 1.5;
+        text-decoration: none;
+    }
+
+    .anthology-pagination .page-item.active .page-link {
+        background: var(--sf);
+        border-color: var(--sf);
+        color: #fff;
+    }
+
+    .anthology-pagination .page-item .page-link:hover {
+        background: var(--sf-light);
+        color: var(--sf);
+    }
+
+    .anthology-pagination .page-item.disabled .page-link {
+        opacity: .45;
+        pointer-events: none;
+    }
+
+    /* Sidebar */
+    .sidebar-box {
+        background: var(--card-bg);
+        border: 1px solid var(--bdr);
+        border-radius: 10px;
+        overflow: hidden;
+        margin-bottom: 1.1rem;
+    }
+
+    .sb-header {
+        padding: .75rem 1.15rem;
+        border-bottom: 1px solid var(--bdr);
+        font-family: 'Noto Serif SC', serif;
+        font-size: .875rem;
+        font-weight: 600;
+        color: var(--ink-soft);
+        letter-spacing: .04em;
+        display: flex;
+        align-items: center;
+        gap: .45rem;
+    }
+
+    .sb-header::before {
+        content: '';
+        display: block;
+        width: 3px;
+        height: 13px;
+        background: var(--sf);
+        border-radius: 2px;
+    }
+
+    .author-ul {
+        list-style: none;
+        padding: .35rem 0;
+        margin: 0;
+    }
+
+    .author-ul li a {
+        display: flex;
+        align-items: center;
+        gap: .6rem;
+        padding: .45rem 1.15rem;
+        text-decoration: none;
+        transition: background .15s;
+    }
+
+    .author-ul li a:hover {
+        background: var(--sf-pale);
+    }
+
+    .au-avatar {
+        width: 28px;
+        height: 28px;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: .68rem;
+        font-weight: 700;
+        color: #fff;
+        flex-shrink: 0;
+    }
+
+    .au-avatar-img {
+        width: 28px;
+        height: 28px;
+        border-radius: 50%;
+        object-fit: cover;
+        flex-shrink: 0;
+    }
+
+    .au-name {
+        font-size: .8rem;
+        color: var(--ink-soft);
+        font-weight: 500;
+        display: block;
+    }
+
+    .au-count {
+        font-size: .7rem;
+        color: var(--ink-muted);
+    }
+
+    /* Search */
+    .search-wrap {
+        background: var(--card-bg);
+        border: 1px solid var(--bdr);
+        border-radius: 10px;
+        padding: .85rem 1.1rem;
+        margin-bottom: 1.1rem;
+    }
+
+    .search-input {
+        width: 100%;
+        border: 1px solid var(--bdr);
+        border-radius: 6px;
+        padding: .45rem .9rem;
+        font-size: .83rem;
+        font-family: 'Noto Sans SC', sans-serif;
+        background: var(--sf-pale);
+        color: var(--ink);
+        outline: none;
+        transition: border-color .2s;
+    }
+
+    .search-input:focus {
+        border-color: var(--sf);
+        background: #fff;
+    }
+
+    .search-input::placeholder {
+        color: var(--ink-muted);
+    }
+
+    @media (max-width:768px) {
+        .anthology-card {
+            flex-direction: column;
+        }
+
+        .card-cover {
+            width: 100%;
+            min-width: unset;
+            height: 90px;
+        }
+    }
 </style>
 </style>
 @endpush
 @endpush
 
 
@@ -268,13 +440,13 @@ body { background: var(--sf-pale) !important; font-family: 'Noto Sans SC', sans-
                     {{-- Cover --}}
                     {{-- Cover --}}
                     <div class="card-cover" style="{{ $item['cover_image'] ? '' : 'background: ' . $item['cover_gradient'] }}">
                     <div class="card-cover" style="{{ $item['cover_image'] ? '' : 'background: ' . $item['cover_gradient'] }}">
                         @if($item['cover_image'])
                         @if($item['cover_image'])
-                            <img src="{{ $item['cover_image'] }}" alt="{{ $item['title'] }}">
+                        <img src="{{ $item['cover_image'] }}" alt="{{ $item['title'] }}">
                         @else
                         @else
-                            <div class="cover-text-wrap">
-                                <div class="cover-title">{{ $item['title'] }}</div>
-                                <div class="cover-divider"></div>
-                                <div class="cover-sub">{{ $item['subtitle'] ?? '' }}</div>
-                            </div>
+                        <div class="cover-text-wrap">
+                            <div class="cover-title">{{ $item['title'] }}</div>
+                            <div class="cover-divider"></div>
+                            <div class="cover-sub">{{ $item['subtitle'] ?? '' }}</div>
+                        </div>
                         @endif
                         @endif
                     </div>
                     </div>
 
 
@@ -286,11 +458,11 @@ body { background: var(--sf-pale) !important; font-family: 'Noto Sans SC', sans-
                         @endif
                         @endif
                         <div class="card-author">
                         <div class="card-author">
                             @if(!empty($item['author']['avatar']))
                             @if(!empty($item['author']['avatar']))
-                                <img src="{{ $item['author']['avatar'] }}" class="a-avatar-img" alt="">
+                            <img src="{{ $item['author']['avatar'] }}" class="a-avatar-img" alt="">
                             @else
                             @else
-                                <div class="a-avatar" style="background: {{ $item['author']['color'] }}">
-                                    {{ $item['author']['initials'] }}
-                                </div>
+                            <div class="a-avatar" style="background: {{ $item['author']['color'] }}">
+                                {{ $item['author']['initials'] }}
+                            </div>
                             @endif
                             @endif
                             <span class="a-name">{{ $item['author']['name'] }}</span>
                             <span class="a-name">{{ $item['author']['name'] }}</span>
                         </div>
                         </div>
@@ -338,9 +510,9 @@ body { background: var(--sf-pale) !important; font-family: 'Noto Sans SC', sans-
                         <li>
                         <li>
                             <a href="#">
                             <a href="#">
                                 @if(!empty($author['avatar']))
                                 @if(!empty($author['avatar']))
-                                    <img src="{{ $author['avatar'] }}" class="au-avatar-img" alt="">
+                                <img src="{{ $author['avatar'] }}" class="au-avatar-img" alt="">
                                 @else
                                 @else
-                                    <div class="au-avatar" style="background: {{ $author['color'] }}">{{ $author['initials'] }}</div>
+                                <div class="au-avatar" style="background: {{ $author['color'] }}">{{ $author['initials'] }}</div>
                                 @endif
                                 @endif
                                 <div>
                                 <div>
                                     <span class="au-name">{{ $author['name'] }}</span>
                                     <span class="au-name">{{ $author['name'] }}</span>

+ 17 - 16
api-v12/resources/views/library/anthology/pagination.blade.php

@@ -1,33 +1,34 @@
+{{-- api-v12/resources/views/library/anthology/pagination.blade.php --}}
 @if ($paginator->hasPages())
 @if ($paginator->hasPages())
 <ul class="pagination">
 <ul class="pagination">
     {{-- Previous --}}
     {{-- Previous --}}
     @if ($paginator->onFirstPage())
     @if ($paginator->onFirstPage())
-        <li class="page-item disabled"><span class="page-link">«</span></li>
+    <li class="page-item disabled"><span class="page-link">«</span></li>
     @else
     @else
-        <li class="page-item"><a class="page-link" href="{{ $paginator->previousPageUrl() }}">«</a></li>
+    <li class="page-item"><a class="page-link" href="{{ $paginator->previousPageUrl() }}">«</a></li>
     @endif
     @endif
 
 
     {{-- Pages --}}
     {{-- Pages --}}
     @foreach ($elements as $element)
     @foreach ($elements as $element)
-        @if (is_string($element))
-            <li class="page-item disabled"><span class="page-link">{{ $element }}</span></li>
-        @endif
-        @if (is_array($element))
-            @foreach ($element as $page => $url)
-                @if ($page == $paginator->currentPage())
-                    <li class="page-item active"><span class="page-link">{{ $page }}</span></li>
-                @else
-                    <li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
-                @endif
-            @endforeach
-        @endif
+    @if (is_string($element))
+    <li class="page-item disabled"><span class="page-link">{{ $element }}</span></li>
+    @endif
+    @if (is_array($element))
+    @foreach ($element as $page => $url)
+    @if ($page == $paginator->currentPage())
+    <li class="page-item active"><span class="page-link">{{ $page }}</span></li>
+    @else
+    <li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
+    @endif
+    @endforeach
+    @endif
     @endforeach
     @endforeach
 
 
     {{-- Next --}}
     {{-- Next --}}
     @if ($paginator->hasMorePages())
     @if ($paginator->hasMorePages())
-        <li class="page-item"><a class="page-link" href="{{ $paginator->nextPageUrl() }}">»</a></li>
+    <li class="page-item"><a class="page-link" href="{{ $paginator->nextPageUrl() }}">»</a></li>
     @else
     @else
-        <li class="page-item disabled"><span class="page-link">»</span></li>
+    <li class="page-item disabled"><span class="page-link">»</span></li>
     @endif
     @endif
 </ul>
 </ul>
 @endif
 @endif

+ 1 - 0
api-v12/resources/views/library/anthology/show.blade.php

@@ -1,3 +1,4 @@
+{{-- api-v12/resources/views/library/anthology/show.blade.php --}}
 @extends('library.layouts.app')
 @extends('library.layouts.app')
 
 
 @section('title', $anthology['title'] . ' · 巴利书库')
 @section('title', $anthology['title'] . ' · 巴利书库')

+ 1 - 0
api-v12/resources/views/library/book/read.blade.php

@@ -1,3 +1,4 @@
+{{-- api-v12/resources/views/library/book/read.blade.php --}}
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
 <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
 
 

+ 112 - 0
api-v12/resources/views/library/search.blade.php

@@ -0,0 +1,112 @@
+{{-- resources/views/library/search.blade.php
+     全站统一搜索结果页。
+     路由:/library/search?q=&type=&category=&lang=
+     原 wiki/search.blade.php 移至此处,@extends 路径更新,
+     搜索框改用 <x-ui.search-input>,空状态改用 <x-ui.empty-state>。
+--}}
+@extends('library.wiki.layouts.app')
+
+@section('title', $query ? '"' . $query . '" 的搜索结果 · WikiPāli' : '搜索 · WikiPāli')
+
+@section('wiki-content')
+
+{{-- 搜索框 --}}
+<div class="wiki-search-bar-wrap">
+    <x-ui.search-input
+        :action="route('library.search')"
+        :value="$query"
+        placeholder="搜索条目、巴利文、梵文…"
+        :autofocus="true"
+        :hidden-fields="array_filter(['category' => $category !== 'all' ? $category : null])" />
+</div>
+
+{{-- 结果摘要 --}}
+<div class="wiki-search-summary">
+    @if ($query)
+        搜索 <strong>「{{ $query }}」</strong>
+        @if ($pagination['total'] > 0)
+            ,共找到 <strong>{{ $pagination['total'] }}</strong> 条结果
+            @if ($pagination['last_page'] > 1)
+                (第 {{ $pagination['current_page'] }} / {{ $pagination['last_page'] }} 页)
+            @endif
+        @else
+            ,未找到相关条目
+        @endif
+    @endif
+</div>
+
+{{-- 结果列表 --}}
+@if (count($results) > 0)
+
+    <div class="wiki-card wiki-search-results">
+        @foreach ($results as $result)
+            <x-wiki.search-result-card :result="$result" :lang="$lang" />
+        @endforeach
+    </div>
+
+    {{-- 分页 --}}
+    @if ($pagination['last_page'] > 1)
+        <x-wiki.pagination
+            :pagination="$pagination"
+            routeName="library.search"
+            :queryParams="array_filter([
+                'q'        => $query,
+                'lang'     => $lang,
+                'category' => $category !== 'all' ? $category : null,
+            ])" />
+    @endif
+
+@else
+
+    <div class="wiki-card">
+        <x-ui.empty-state
+            title="未找到相关条目"
+            desc="请尝试其他关键词" />
+    </div>
+
+@endif
+
+@endsection
+
+@section('wiki-sidebar')
+
+{{-- 分类筛选 --}}
+<div class="wiki-sidebar-section">
+    <div class="wiki-sidebar-title">按分类筛选</div>
+    <ul class="wiki-cat-list">
+        <li>
+            <a href="{{ route('library.search', ['q' => $query]) }}"
+               class="{{ $category === 'all' ? 'active' : '' }}">
+                全部
+                <span class="wiki-cat-count">{{ $pagination['total'] }}</span>
+            </a>
+        </li>
+        @isset($filters)
+            @foreach ($filters as $cat)
+            <li>
+                <a href="{{ route('library.search', ['q' => $query, 'category' => $cat->key]) }}"
+                   class="{{ $category === $cat->key ? 'active' : '' }}">
+                    {{ $cat->key }}
+                    <span class="wiki-cat-count">{{ $cat->doc_count }}</span>
+                </a>
+            </li>
+            @endforeach
+        @endisset
+    </ul>
+</div>
+
+{{-- 近似词条(无结果时显示) --}}
+@if (count($results) === 0 && $query)
+<div class="wiki-sidebar-section">
+    <div class="wiki-sidebar-title">你可能在找</div>
+    <ul class="wiki-related-list">
+        <li>
+            <a href="{{ route('library.search', ['q' => substr($query, 0, -1), 'lang' => $lang]) }}">
+                {{ substr($query, 0, -1) }}
+            </a>
+        </li>
+    </ul>
+</div>
+@endif
+
+@endsection

+ 80 - 0
api-v12/resources/views/library/wiki/home.blade.php

@@ -0,0 +1,80 @@
+{{-- resources/views/wiki/home.blade.php
+     Wiki 门户首页。
+     布局:单栏居中,法轮图标 + 标题 + 搜索框 + 热门标签 + 语言选择。
+     所有样式来自 modules/_wiki.css,无内联 <style>。
+--}}
+@extends('library.wiki.layouts.app')
+@extends('wiki.layouts.app')
+
+@section('title', 'WikiPāli · 佛教百科-重构')
+
+@section('wiki-content')
+<div class="wiki-home-container">
+
+    {{-- 法轮图标 --}}
+    <div class="wiki-home-wheel">
+        <img src="{{ asset('assets/images/dhamma-wheel.svg') }}"
+            alt="Dharma Wheel"
+            class="wiki-home-wheel-img">
+    </div>
+
+    {{-- 欢迎标题 --}}
+    <div class="wiki-home-title">
+        <h1>佛教百科</h1>
+        <p class="text-muted">探索佛法智慧 · 开启觉悟之门</p>
+    </div>
+
+    {{-- 搜索框 --}}
+    <div class="wiki-home-search">
+        <x-wiki.search-box
+            :action="route('library.search')"
+            placeholder="搜索佛法词条、经典、人物..."
+            button-text="搜索"
+            size="lg" />
+    </div>
+
+    {{-- 热门搜索标签 --}}
+    @isset($hotTags)
+    <div class="wiki-home-hot-tags">
+        <span class="text-muted me-2">热门:</span>
+        @foreach($hotTags as $tag)
+        <a href="{{ route('library.search', ['q' => $tag, 'type' => 'wiki']) }}"
+            class="wiki-hot-tag">
+            {{ $tag }}
+        </a>
+        @endforeach
+    </div>
+    @endisset
+
+    {{-- 语言选择器 --}}
+    <div class="wiki-home-languages">
+        <div class="wiki-home-divider">
+            <span>以您的语言阅读佛教百科</span>
+        </div>
+        <div class="wiki-language-tags">
+            @foreach($languages as $lang)
+            <a href="{{ route('library.wiki.index', ['lang' => $lang['code']]) }}"
+                class="wiki-language-tag {{ ($currentLang ?? 'zh-Hans') === $lang['code'] ? 'active' : '' }}">
+                {{ $lang['name'] }}
+            </a>
+            @endforeach
+        </div>
+    </div>
+
+    {{-- 统计信息 --}}
+    @isset($stats)
+    <div class="wiki-home-stats">
+        <span class="text-muted">
+            📚 {{ number_format($stats['total_articles'] ?? 0) }} 词条
+            @if(isset($stats['today_updates']))
+            · 🆕 今日更新 {{ $stats['today_updates'] }}
+            @endif
+            @if(isset($stats['contributors']))
+            · 👥 {{ number_format($stats['contributors']) }} 位贡献者
+            @endif
+        </span>
+    </div>
+    @endisset
+
+</div>
+@endsection

+ 68 - 0
api-v12/resources/views/library/wiki/index.blade.php

@@ -0,0 +1,68 @@
+{{-- resources/views/wiki/index.blade.php --}}
+@extends('library.wiki.layouts.app')
+
+@section('title', 'WikiPāli · 巴利佛典百科')
+
+@section('wiki-content')
+{{-- 搜索框组件 --}}
+<div class="wiki-search-wrapper">
+    <x-wiki.search-box
+        :action="route('library.search')"
+        placeholder="搜索佛法词条、经典、人物..."
+        button-text="搜索"
+        size="lg" />
+</div>
+{{-- 今日条目 --}}
+<div class="wiki-today-banner">
+    <div class="wiki-today-icon">☸</div>
+    <div class="wiki-today-body">
+        <div class="wiki-today-label">今日条目</div>
+        <div class="wiki-today-title">{{ $today['meaning'] }}({{ $today['word'] }})</div>
+        <div class="wiki-today-snippet">
+            {!! Str::limit(strip_tags($today['content']), 120) !!}
+        </div>
+        <a class="wiki-today-link"
+            href="{{ route('library.wiki.show', [$today['lang'], $today['word']]) }}">
+            阅读完整条目 →
+        </a>
+    </div>
+</div>
+
+{{-- 精选条目 --}}
+<div class="wiki-card">
+    <div class="wiki-sidebar-title" style="margin-bottom: 14px;">精选条目</div>
+    <div class="wiki-featured-grid">
+        @foreach ($featured as $item)
+        <a class="wiki-featured-card"
+            href="{{ route('library.wiki.show', [$item['lang'], $item['word']]) }}">
+            <div class="wiki-featured-label">{{ $item['category'] }}</div>
+            <div class="wiki-featured-title">{{ $item['zh'] }}</div>
+            <div class="wiki-featured-pali">{{ $item['word'] }}</div>
+        </a>
+        @endforeach
+    </div>
+</div>
+
+@endsection
+
+@section('wiki-sidebar')
+
+<div class="wiki-sidebar-section">
+    <div class="wiki-sidebar-title">统计</div>
+    <table class="wiki-meta-table">
+        <tr>
+            <td>条目总数</td>
+            <td>{{ number_format($stats['total']) }}</td>
+        </tr>
+        <tr>
+            <td>本月新增</td>
+            <td>{{ $stats['this_month'] }}</td>
+        </tr>
+        <tr>
+            <td>贡献者</td>
+            <td>{{ $stats['contributors'] }}</td>
+        </tr>
+    </table>
+</div>
+
+@endsection

+ 78 - 0
api-v12/resources/views/library/wiki/layouts/app.blade.php

@@ -0,0 +1,78 @@
+{{-- resources/views/wiki/layouts/app.blade.php
+     Wiki 栏目布局中间层。
+     继承 layouts.library(提供 navbar + footer 外壳)。
+     负责:wiki 三栏容器、wiki CSS 注入、term-drawer 全局组件。
+--}}
+@extends('layouts.library')
+
+@push('styles')
+    @vite('resources/css/modules/_wiki.css')
+@endpush
+
+@section('content')
+
+<div class="container-xl wiki-layout">
+
+    {{-- 左侧边栏 --}}
+    @hasSection('wiki-sidebar-left')
+        <aside class="wiki-sidebar-left">
+            @yield('wiki-sidebar-left')
+        </aside>
+    @else
+        @if(isset($lang))
+        <aside class="wiki-sidebar-left">
+
+            @isset($categories)
+            <div class="wiki-sidebar-section">
+                <div class="wiki-sidebar-title">分类浏览</div>
+                <ul class="wiki-cat-list">
+                    @foreach ($categories as $cat)
+                    <li>
+                        <a href="{{ route('library.wiki.index', ['lang' => $lang]) }}?category={{ $cat['slug'] }}"
+                           class="{{ (request('category', 'all') === $cat['slug']) ? 'active' : '' }}">
+                            {{ $cat['label'] }}
+                            @if(isset($cat['count']))
+                                <span class="wiki-cat-count">{{ $cat['count'] }}</span>
+                            @endif
+                        </a>
+                    </li>
+                    @endforeach
+                </ul>
+            </div>
+            @endisset
+
+            @isset($recentUpdates)
+            <div class="wiki-sidebar-section">
+                <div class="wiki-sidebar-title">最近更新</div>
+                <ul class="wiki-cat-list">
+                    @foreach ($recentUpdates as $item)
+                    <li>
+                        <a href="{{ route('library.wiki.show', [$item['lang'], $item['word']]) }}">
+                            {{ $item['word'] }}
+                        </a>
+                    </li>
+                    @endforeach
+                </ul>
+            </div>
+            @endisset
+
+        </aside>
+        @endif
+    @endif
+
+    {{-- 主内容区 --}}
+    <main class="wiki-main">
+        @yield('wiki-content')
+    </main>
+
+    {{-- 右侧边栏 --}}
+    <aside class="wiki-sidebar-right">
+        @yield('wiki-sidebar')
+    </aside>
+
+</div>
+
+{{-- 术语抽屉(移动端,所有阅读页公用,在此统一挂载) --}}
+<x-wiki.term-drawer />
+
+@endsection

+ 80 - 0
api-v12/resources/views/library/wiki/show.blade.php

@@ -0,0 +1,80 @@
+{{-- resources/views/wiki/show.blade.php --}}
+@extends('library.wiki.layouts.app')
+
+@section('title', $entry['meaning'] . '(' . $entry['word'] . ')· WikiPāli')
+
+@section('wiki-content')
+{{-- 搜索框组件 --}}
+<div class="wiki-search-wrapper">
+    <x-wiki.search-box
+        :action="route('library.search')"
+        placeholder="搜索佛法词条、经典、人物..."
+        button-text="搜索"
+        size="lg" />
+</div>
+<article class="wiki-card">
+
+    {{-- 条目头部 --}}
+    <x-wiki.entry-header :entry="$entry" />
+
+    {{-- 语言版本切换 --}}
+    <x-wiki.entry-langs :langs="$entry['langs']" :current="$entry['lang']" />
+
+    {{-- 正文 --}}
+    <div class="wiki-content-body">
+        {!! $entry['content'] !!}
+    </div>
+
+    {{-- 标签 --}}
+    <div class="wiki-tags">
+        @foreach ($entry['tags'] as $tag)
+        <a class="wiki-tag" href="{{ route('library.wiki.index') }}?tag={{ $tag }}">
+            {{ $tag }}
+        </a>
+        @endforeach
+    </div>
+
+</article>
+
+@endsection
+
+@section('wiki-sidebar')
+
+{{-- 目录 --}}
+<div class="wiki-sidebar-section">
+    <div class="wiki-sidebar-title">目录</div>
+    <ul class="wiki-toc-list">
+        @foreach ($entry['toc'] as $i => $item)
+        <li class="toc-level-{{ $item['level'] }}">
+            <a href="#{{ $item['id'] }}">
+                <span class="wiki-toc-num">{{ $i + 1 }}</span>
+                {{ $item['text'] }}
+            </a>
+        </li>
+        @endforeach
+    </ul>
+</div>
+
+{{-- 相关条目 --}}
+<x-wiki.related-entries :entries="$entry['related']" />
+
+{{-- 条目元信息 --}}
+<div class="wiki-sidebar-section">
+    <div class="wiki-sidebar-title">条目信息</div>
+    <table class="wiki-meta-table">
+        <tr>
+            <td>分类</td>
+            <td>{{ $entry['category'] }}</td>
+        </tr>
+        <tr>
+            <td>质量</td>
+            <td><x-wiki.quality-badge :quality="$entry['quality']" /></td>
+        </tr>
+    </table>
+</div>
+
+@endsection
+
+@push('scripts')
+@vite('resources/js/modules/term-tooltip.js')
+@endpush

+ 1 - 1
api-v12/routes/web.php

@@ -11,7 +11,7 @@ use App\Http\Controllers\DownloadController;
 use App\Http\Controllers\Library\AnthologyController;
 use App\Http\Controllers\Library\AnthologyController;
 use App\Http\Controllers\Library\AnthologyReadController;
 use App\Http\Controllers\Library\AnthologyReadController;
 use App\Http\Controllers\Library\BookController;
 use App\Http\Controllers\Library\BookController;
-use App\Http\Controllers\WikiController;
+use App\Http\Controllers\Library\WikiController;
 use App\Http\Controllers\Library\SearchController;
 use App\Http\Controllers\Library\SearchController;
 
 
 /*
 /*

+ 8 - 3
api-v12/vite.config.js

@@ -1,10 +1,15 @@
-import { defineConfig } from "vite";
-import laravel from "laravel-vite-plugin";
+// vite.config.js
+import { defineConfig } from 'vite';
+import laravel from 'laravel-vite-plugin';
 
 
 export default defineConfig({
 export default defineConfig({
     plugins: [
     plugins: [
         laravel({
         laravel({
-            input: ["resources/css/main.css", "resources/js/app.js"],
+            input: [
+                'resources/css/library.css',   // library/* + blog 列表页
+                'resources/css/reader.css',    // 全站阅读页(待建)
+                'resources/js/app.js',
+            ],
             refresh: true,
             refresh: true,
         }),
         }),
     ],
     ],