เอกสารที่อยู่นอก repo มักตามโค้ดไม่ทัน
ที่ผ่านมาเราก็มักเก็บเอกสารโปรเจกต์ไว้ใน Google Docs, Notion หรือ Wiki ของ GitHub แยกจากโค้ด พอแก้โค้ดก็ไม่มีใครกลับไปแก้เอกสาร ผ่านไปไม่กี่เดือนเอกสารก็เล่าระบบเวอร์ชันเก่า
ทุกวันนี้เอกสารเริ่มย้ายกลับมาอยู่ใน repo เพราะ AI agent อย่าง Claude Code อ่านไฟล์ใน repo ได้เอง เราจึงเขียนไว้ใน CLAUDE.md หรือโฟลเดอร์อย่าง .ai/documents/ แล้วแก้ไปพร้อมโค้ดใน commit เดียวกัน
ปัญหาคือ markdown ใน repo เหมาะกับคนที่เปิด editor อยู่แล้ว ถ้าเป็นคนในทีมที่ไม่ได้เขียนโค้ด เช่น PM, ทีม support หรือลูกค้าที่อยากอ่านว่าระบบทำงานยังไง ก็ต้องมีสิทธิ์ใน GitHub ก่อน แล้วยังต้องคลิกไล่โฟลเดอร์หาไฟล์เอง
laravel-ai-docs เปิดโฟลเดอร์ markdown เป็นหน้าเว็บในแอป
แพ็กเกจนี้อ่านโฟลเดอร์ที่ชี้ไว้ (ค่าเริ่มต้นคือ .ai/documents) แล้วแสดงเป็นเว็บเอกสารที่ /docs ในแอปเดิมของเรา ไม่ต้อง build หรือ deploy เว็บแยก
- ไฟล์
.mdทุกไฟล์เป็นหนึ่งหน้า โฟลเดอร์กลายเป็นกลุ่มในเมนูด้านซ้าย ซ้อนกันได้ลึกเท่าที่เราจัดไว้ - ชื่อหน้าในเมนูมาจากหัวข้อ
#บรรทัดแรก ไม่ต้องมีไฟล์ตั้งค่าเมนู เพิ่มไฟล์ใหม่ก็ขึ้นในเมนูเอง - รูปที่วางไว้ข้างไฟล์ markdown แสดงได้เลย ไม่ต้องก๊อปไปไว้ใน
public/ - ลิงก์ระหว่างไฟล์ เช่น
[ดูเรื่อง webhook](../integrations/webhooks.md#retry)กลายเป็นลิงก์ไปหน้านั้นในเว็บ

เมนูด้านซ้ายมาจากโฟลเดอร์ ด้านขวาเป็นสารบัญหัวข้อของหน้า ที่ไฮไลต์ตามตำแหน่งที่เลื่อนอ่าน
แต่ละไฟล์ใส่ front matter ได้ถ้าอยากกำหนดเอง แต่ไม่จำเป็น title ใช้เป็นชื่อหน้า nav เป็นชื่อในเมนู และ order เรียงลำดับในกลุ่ม
---
nav: Payment retries
order: 10
---
# Payment retries — ลำดับการลองตัดเงินซ้ำ
ถ้าไม่ใส่ nav แพ็กเกจตัดชื่อจาก # ที่ตัว — หรือ : ให้เอง หน้านี้จึงขึ้นในเมนูว่า Payment retries
ติดตั้ง
แพ็กเกจต้องการ PHP 8.4 ขึ้นไป Laravel 12 หรือ 13 และแอปต้องใช้ Inertia v2/v3 กับ React 18/19 เพราะหน้าเอกสารเป็นหน้า Inertia ไม่ใช่ Blade
composer require phattarachai/laravel-ai-docs
php artisan vendor:publish --tag=ai-docs-config
php artisan vendor:publish --tag=ai-docs-inertia
npm install mermaid
tag ai-docs-inertia ต้อง publish ทุกครั้ง คำสั่งนี้สร้างไฟล์เดียวคือ resources/js/pages/AiDocs.jsx เพราะ Inertia หาหน้าได้เฉพาะในโฟลเดอร์ pages ส่วนโค้ดจริงยังอยู่ใน vendor/ แล้วเรียกผ่าน alias ใน Vite แอปของเราจึงไม่มีสำเนาที่ต้องตามอัปเดต
import path from 'node:path'
export default defineConfig({
resolve: {
alias: {
'@ai-docs': path.resolve(__dirname, 'vendor/phattarachai/laravel-ai-docs/resources/js/ai-docs'),
},
},
})
use Illuminate\Http\Request;
use Phattarachai\AiDocs\AiDocs;
public function boot(): void
{
AiDocs::auth(fn (Request $request): bool => $request->user()?->is_admin === true);
}
สุดท้ายเช็กแล้ว build
php artisan ai-docs:doctor
npm run build
ai-docs:doctor เช็ก route, gate, โฟลเดอร์เอกสาร, ไฟล์ที่ publish, alias ใน Vite และ mermaid แล้วบอกว่าข้อไหนยังขาด CSS ของแพ็กเกจเป็น CSS ธรรมดาที่ครอบไว้ใต้ .doc-root ไม่ต้องเพิ่ม @source ใน Tailwind และไม่ชนกับ CSS ของแอป
ยังไม่ตั้ง gate ก็ยังไม่มีใครเปิดได้
เอกสารภายในมักมีรายละเอียดที่ไม่ควรหลุดออกไป แพ็กเกจจึงปิดไว้ก่อน ถ้ายังไม่ได้เรียก AiDocs::auth() แพ็กเกจปฏิเสธทุก request รวมถึงตัวเราเองด้วย
- คนที่ยังไม่ login จะไปหน้า login ของแอป แล้วกลับมาหน้าเอกสารที่ตั้งใจเปิดหลัง login เสร็จ
- คนที่ login แล้วแต่ gate ไม่ให้ผ่านได้
403 - ตั้ง
AI_DOCS_ENABLED=falseแล้วแพ็กเกจไม่ลงทะเบียน route เลย/docsจะเป็น404
แพ็กเกจต่อ middleware ที่เช็ก gate ไว้ท้ายสุดเสมอ ถึงจะแก้ ai-docs.middleware ใน config ก็ลบออกไม่ได้
ค้นหาทั้งชุดด้วย ⌘K
กด ⌘K แล้วพิมพ์คำที่หา แพ็กเกจค้นทุกหัวข้อในทุกหน้า แสดงข้อความรอบ ๆ คำที่เจอพร้อมไฮไลต์ กด Enter แล้วกระโดดไปที่หัวข้อนั้นเลย ดัชนีค้นหาสร้างฝั่งเซิร์ฟเวอร์ ไม่ต้องตั้ง Algolia หรือบริการค้นหาภายนอก

mermaid, callout และโค้ดแสดงครบเหมือนบน GitHub
เขียน fence ```mermaid แล้วได้แผนภาพ ทั้งโหมดสว่างและมืดใช้สีเข้ากับหน้าเว็บ และมีสีสำเร็จรูปให้เลือกใส่ทีละ node decision, ok, bad และ actor
```mermaid
flowchart LR
C[ตัดเงินไม่ผ่าน] --> R{ลองใหม่ได้ไหม}:::decision
R -->|ได้| S[ตั้งเวลาลองใหม่] --> D[ชำระแล้ว]:::ok
R -->|ไม่ได้| X[ตัดเป็นหนี้สูญ]:::bad
```
callout แบบ GitHub (> [!NOTE], > [!TIP], > [!WARNING] …) แสดงเป็นกล่องพร้อมหัวข้อ ส่วนโค้ดไฮไลต์ฝั่งเซิร์ฟเวอร์ด้วย tempest/highlight เบราว์เซอร์ไม่ต้องโหลด JavaScript สำหรับไฮไลต์

ตาราง แผนภาพ และรูปทุกอันมีปุ่มเปิดเต็มจอ ไว้ดู sequence diagram ยาว ๆ ที่ไม่มีทางพอดีคอลัมน์

หน้าเว็บมีโหมดมืด จำค่าไว้ในเบราว์เซอร์ และเก็บไว้ที่ .doc-root ไม่ไปยุ่งกับธีมของแอปเรา

SVG ที่วาดเองใช้ฟอนต์ไทยตามหน้าเว็บ
mermaid เหมาะกับ flow แต่แผนผังระบบที่มีป้ายสถานะ เส้นหลายสี และป้ายภาษาไทย วาดเองเป็น SVG ง่ายกว่า วางไฟล์ .svg ไว้ข้าง markdown แล้วใส่ inline ที่ title ของรูป

แพ็กเกจเขียน SVG ลงในหน้าโดยตรงแทนการโหลดผ่าน <img> ข้อความใน SVG จึงใช้ฟอนต์เดียวกับหน้าเว็บ ภาษาไทยไม่เพี้ยน เปลี่ยนสีตามโหมดมืดได้ถ้าวาดด้วยตัวแปร --doc-* ลิงก์ใน SVG กดไปหน้าเอกสารอื่นได้ และข้อความใน SVG ค้นหาด้วย ⌘K เจอ ส่วน wide ให้แผนผังกว้างเต็มพื้นที่เมื่อหน้านั้นไม่มีสารบัญด้านขวา

ก่อนใส่ลงหน้า แพ็กเกจคัด SVG ทีละ element เก็บไว้แค่ส่วนที่ใช้วาด ตัด <script>, <foreignObject>, attribute on* และลิงก์ภายนอกทิ้ง ไฟล์ที่ใครก็แก้ใน repo ได้จึงไม่กลายเป็นช่องทางรันสคริปต์ในหน้าเอกสาร
แยกเอกสารกับงานชั่วคราวเป็นคนละ panel
เอกสารระบบเราดูแลไปเรื่อย ๆ ส่วนโฟลเดอร์ task ที่จดงานแต่ละรอบเขียนเสร็จก็จบ ถ้ารวมไว้ที่เดียว task จะกลบเอกสาร แพ็กเกจจึงให้แยกเป็นหลาย panel แต่ละ panel มี URL เมนู และดัชนีค้นหาของตัวเอง สลับกันได้จากปุ่มด้านบน
'panels' => [
'docs' => ['root' => '.ai/documents', 'label' => 'Documents'],
'tasks' => ['root' => '.ai/tasks', 'label' => 'Tasks'],
],

ลิงก์ข้าม panel ได้ task ลิงก์ไปเอกสารระบบที่เกี่ยวข้อง แล้วเอกสารลิงก์กลับมาที่ task ได้
ปุ่มคัดลอก path ไว้ส่งต่อให้ AI
ทุกหน้ามีปุ่มคัดลอก path ของไฟล์ใน repo เช่น .ai/documents/billing/payment-retries.md เอาไปวางต่อท้าย @ ใน Claude Code หรือ Cursor ได้ทันที เวลาคุยงานกับทีมก็ส่งลิงก์หน้าเว็บให้คน และส่ง path ให้ AI จากหน้าเดียวกัน
อีกปุ่มคือพิมพ์หรือบันทึกเป็น PDF ผ่านเบราว์เซอร์ แพ็กเกจซ่อนเมนูกับปุ่มต่าง ๆ ตอนพิมพ์ และไม่ตัดตารางหรือแผนภาพข้ามหน้า ถ้าแผนภาพไหนใหญ่ไป ใส่ print-70 ให้ย่อเหลือราว 70% ของหน้ากระดาษ
จัดโฟลเดอร์ให้ทั้งคนและ AI อ่านง่าย
หน้าเว็บดีได้เท่าที่โฟลเดอร์จัดไว้ดี แพ็กเกจมี docs/authoring.md เล่าวิธีจัดที่ผมใช้กับโปรเจกต์ของตัวเอง สรุปสั้น ๆ คือ
- โฟลเดอร์ต้องมีอย่างน้อยสองไฟล์ ไฟล์เดียวให้วางไว้ชั้นบนสุด เรื่องที่ใช้ทั้งระบบอย่าง auth หรือ testing ก็วางชั้นบนสุดเหมือนกัน
index.mdคือแผนที่ มีบรรทัดละไฟล์ บอกว่าไฟล์นั้นดูแลเรื่องอะไร เพิ่ม ย้าย หรือลบไฟล์เมื่อไหร่ก็แก้ index ใน commit เดียวกัน เพราะ AI มักอ่าน index ก่อนแล้วหยุดแค่นั้น- หนึ่งไฟล์ไม่เกิน 500 บรรทัด ใกล้ถึงเมื่อไหร่แปลว่ามีอีกเรื่องซ่อนอยู่ ให้แยกเป็นไฟล์ใหม่ที่ตั้งชื่อตามเรื่องนั้น
ถ้าให้ Claude Code เขียนเอกสารให้ แพ็กเกจมี skill ตัวอย่างใน examples/document-skill ก๊อปไปไว้ที่ .claude/skills/document/SKILL.md แล้วสั่ง "document this" หลังทำฟีเจอร์เสร็จ
คนที่ผ่าน gate อ่านได้ทุกไฟล์ ยกเว้นที่ exclude ไว้
gate บอกได้แค่ว่าใครเข้าเว็บเอกสารได้ พอผ่านแล้วอ่านได้ทุกไฟล์ในโฟลเดอร์ ถ้าในเอกสารมีรหัสผ่านหรือ token ที่ใครเคยแปะไว้ คนที่ผ่าน gate ก็เห็นด้วย เอกสารที่เปิดให้ลูกค้าหรือคนนอกทีมอ่าน จึงควรแยกโฟลเดอร์แล้วใช้ exclude ซ่อนส่วนที่เหลือ
'exclude' => ['operations', 'internal'],
แพ็กเกจตัดโฟลเดอร์ที่ exclude ออกจากเมนู ดัชนีค้นหา URL ตรง และรูปทั้งหมด ส่วนรูปที่แสดงได้ แพ็กเกจส่งผ่าน gate เดียวกันพร้อม X-Content-Type-Options: nosniff และ SVG ได้ Content-Security-Policy ที่ไม่ให้รันสคริปต์
อีกข้อคือแพ็กเกจใช้ได้กับแอป Inertia React เท่านั้น แอปที่ทำด้วย Blade หรือ Livewire อย่างเดียวยังใช้ไม่ได้
GitHub: phattarachai/laravel-ai-docs






