# Progressive Web App (PWA) Guide (ไทย) 🌐 **Languages:** 🇺🇸 [English](../../../../guides/PWA_GUIDE.md) · 🇪🇹 [am](../../../am/docs/guides/PWA_GUIDE.md) · 🇸🇦 [ar](../../../ar/docs/guides/PWA_GUIDE.md) · 🇦🇿 [az](../../../az/docs/guides/PWA_GUIDE.md) · 🇧🇬 [bg](../../../bg/docs/guides/PWA_GUIDE.md) · 🇧🇩 [bn](../../../bn/docs/guides/PWA_GUIDE.md) · 🇧🇦 [bs](../../../bs/docs/guides/PWA_GUIDE.md) · 🇨🇿 [cs](../../../cs/docs/guides/PWA_GUIDE.md) · 🇩🇰 [da](../../../da/docs/guides/PWA_GUIDE.md) · 🇩🇪 [de](../../../de/docs/guides/PWA_GUIDE.md) · 🇬🇷 [el](../../../el/docs/guides/PWA_GUIDE.md) · 🇪🇸 [es](../../../es/docs/guides/PWA_GUIDE.md) · 🇪🇪 [et](../../../et/docs/guides/PWA_GUIDE.md) · 🇮🇷 [fa](../../../fa/docs/guides/PWA_GUIDE.md) · 🇫🇮 [fi](../../../fi/docs/guides/PWA_GUIDE.md) · 🇫🇷 [fr](../../../fr/docs/guides/PWA_GUIDE.md) · 🇮🇪 [ga](../../../ga/docs/guides/PWA_GUIDE.md) · 🇮🇳 [gu](../../../gu/docs/guides/PWA_GUIDE.md) · 🇳🇬 [ha](../../../ha/docs/guides/PWA_GUIDE.md) · 🇮🇱 [he](../../../he/docs/guides/PWA_GUIDE.md) · 🇮🇳 [hi](../../../hi/docs/guides/PWA_GUIDE.md) · 🇭🇷 [hr](../../../hr/docs/guides/PWA_GUIDE.md) · 🇭🇺 [hu](../../../hu/docs/guides/PWA_GUIDE.md) · 🇦🇲 [hy](../../../hy/docs/guides/PWA_GUIDE.md) · 🇮🇩 [id](../../../id/docs/guides/PWA_GUIDE.md) · 🇳🇬 [ig](../../../ig/docs/guides/PWA_GUIDE.md) · 🇮🇹 [it](../../../it/docs/guides/PWA_GUIDE.md) · 🇯🇵 [ja](../../../ja/docs/guides/PWA_GUIDE.md) · 🇬🇪 [ka](../../../ka/docs/guides/PWA_GUIDE.md) · 🇰🇭 [km](../../../km/docs/guides/PWA_GUIDE.md) · 🇮🇳 [kn](../../../kn/docs/guides/PWA_GUIDE.md) · 🇰🇷 [ko](../../../ko/docs/guides/PWA_GUIDE.md) · 🇱🇹 [lt](../../../lt/docs/guides/PWA_GUIDE.md) · 🇱🇻 [lv](../../../lv/docs/guides/PWA_GUIDE.md) · 🇮🇳 [ml](../../../ml/docs/guides/PWA_GUIDE.md) · 🇮🇳 [mr](../../../mr/docs/guides/PWA_GUIDE.md) · 🇲🇾 [ms](../../../ms/docs/guides/PWA_GUIDE.md) · 🇲🇹 [mt](../../../mt/docs/guides/PWA_GUIDE.md) · 🇲🇲 [my](../../../my/docs/guides/PWA_GUIDE.md) · 🇳🇵 [ne](../../../ne/docs/guides/PWA_GUIDE.md) · 🇳🇱 [nl](../../../nl/docs/guides/PWA_GUIDE.md) · 🇳🇴 [no](../../../no/docs/guides/PWA_GUIDE.md) · 🇮🇳 [or](../../../or/docs/guides/PWA_GUIDE.md) · 🇮🇳 [pa](../../../pa/docs/guides/PWA_GUIDE.md) · 🇵🇭 [phi](../../../phi/docs/guides/PWA_GUIDE.md) · 🇵🇱 [pl](../../../pl/docs/guides/PWA_GUIDE.md) · 🇵🇹 [pt](../../../pt/docs/guides/PWA_GUIDE.md) · 🇧🇷 [pt-BR](../../../pt-BR/docs/guides/PWA_GUIDE.md) · 🇷🇴 [ro](../../../ro/docs/guides/PWA_GUIDE.md) · 🇷🇺 [ru](../../../ru/docs/guides/PWA_GUIDE.md) · 🇱🇰 [si](../../../si/docs/guides/PWA_GUIDE.md) · 🇸🇰 [sk](../../../sk/docs/guides/PWA_GUIDE.md) · 🇸🇮 [sl](../../../sl/docs/guides/PWA_GUIDE.md) · 🇷🇸 [sr](../../../sr/docs/guides/PWA_GUIDE.md) · 🇸🇪 [sv](../../../sv/docs/guides/PWA_GUIDE.md) · 🇰🇪 [sw](../../../sw/docs/guides/PWA_GUIDE.md) · 🇮🇳 [ta](../../../ta/docs/guides/PWA_GUIDE.md) · 🇮🇳 [te](../../../te/docs/guides/PWA_GUIDE.md) · 🇹🇷 [tr](../../../tr/docs/guides/PWA_GUIDE.md) · 🇺🇦 [uk-UA](../../../uk-UA/docs/guides/PWA_GUIDE.md) · 🇵🇰 [ur](../../../ur/docs/guides/PWA_GUIDE.md) · 🇺🇿 [uz](../../../uz/docs/guides/PWA_GUIDE.md) · 🇻🇳 [vi](../../../vi/docs/guides/PWA_GUIDE.md) · 🇳🇬 [yo](../../../yo/docs/guides/PWA_GUIDE.md) · 🇨🇳 [zh-CN](../../../zh-CN/docs/guides/PWA_GUIDE.md) · 🇹🇼 [zh-TW](../../../zh-TW/docs/guides/PWA_GUIDE.md) --- OmniRoute พร้อมใช้งานในรูปแบบ Progressive Web App ที่ติดตั้งได้อย่างสมบูรณ์ เมื่อคุณเข้าถึงแดชบอร์ดจากเบราว์เซอร์บนอุปกรณ์เคลื่อนที่ ไม่ว่าจะเป็น Android (Chrome) หรือ iOS (Safari) คุณสามารถเลือก "เพิ่มไปยังหน้าจอหลัก" และรับประสบการณ์การใช้งานเสมือนแอปแบบเนทีฟได้โดยไม่ต้องผ่านร้านค้าแอป ## PWA คืออะไร? Progressive Web App เปลี่ยนแดชบอร์ดเว็บของ OmniRoute ให้มีรูปลักษณ์และการใช้งานเหมือนแอปมือถือแบบเนทีฟ เมื่อติดตั้งแล้ว แอปจะ: - เปิดใช้งานจากหน้าจอหลักด้วยไอคอนของตัวเอง - เปิดแบบเต็มหน้าจอ — ไม่มีแถบที่อยู่หรือ UI ของแท็บเบราว์เซอร์ - ทำงานแบบออฟไลน์ได้ด้วยหน้าสถานะการเชื่อมต่อโดยเฉพาะ - แคชแอสเซ็ตแบบคงที่เพื่อให้โหลดได้เร็วขึ้น - รองรับทั้งแนวตั้งและแนวนอน ## การติดตั้ง ### Android (Chrome) 1. เปิดแดชบอร์ด OmniRoute ใน Chrome: `http://YOUR_IP:20128` 2. Chrome จะแสดงแบนเนอร์ **"เพิ่ม OmniRoute ไปยังหน้าจอหลัก"** โดยอัตโนมัติ หรือ: - แตะเมนู **⋮** (จุดสามจุด) → **"เพิ่มไปยังหน้าจอหลัก"** หรือ **"ติดตั้งแอป"** 3. ยืนยันข้อความแจ้ง 4. OmniRoute จะปรากฏบนหน้าจอหลักของคุณในรูปแบบแอปแบบสแตนด์อโลน ### iOS (Safari) 1. เปิดแดชบอร์ด OmniRoute ใน Safari: `http://YOUR_IP:20128` 2. แตะปุ่ม **แชร์** (กล่องที่มีลูกศร) 3. เลื่อนลงแล้วแตะ **"เพิ่มไปยังหน้าจอโฮม"** 4. ตั้งชื่อแอป (ค่าเริ่มต้นคือ "OmniRoute") แล้วแตะ **เพิ่ม** 5. OmniRoute จะปรากฏบนหน้าจอหลักของคุณพร้อมไอคอนแอป ### เดสก์ท็อป (Chrome / Edge) 1. เปิดแดชบอร์ด OmniRoute 2. คลิก **ไอคอนติดตั้ง** ในแถบที่อยู่ (หรือ ⋮ → "ติดตั้ง OmniRoute...") 3. ยืนยันข้อความแจ้ง 4. OmniRoute จะเปิดเป็นหน้าต่างแบบสแตนด์อโลน — ไม่มีแท็บและไม่มีแถบที่อยู่ ## คุณสมบัติ ### ประสบการณ์แบบเต็มหน้าจอ ไฟล์ manifest ได้รับการกำหนดค่าด้วย `display: "fullscreen"` ซึ่งหมายความว่าแอปที่ติดตั้งแล้วจะใช้พื้นที่ทั้งหน้าจอ — ไม่มีส่วนติดต่อของเบราว์เซอร์และไม่มีการซ้อนทับกับแถบสถานะ ทำให้แดชบอร์ดให้ความรู้สึกเหมือนแอปแบบเนทีฟอย่างแท้จริง ### การรองรับแบบออฟไลน์ OmniRoute มี service worker (`sw.js`) ที่ช่วยจัดการแคชอย่างชาญฉลาด: | ประเภทแอสเซ็ต | กลยุทธ์ | ลักษณะการทำงาน | | --------------------------------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------- | | **เชลล์แอป** | ใช้แคชก่อน | `/`, `/offline`, manifest และไอคอนจะถูกแคชล่วงหน้าระหว่างการติดตั้ง | | **แอสเซ็ตแบบคงที่** (CSS, JS, รูปภาพ, ฟอนต์) | ใช้เครือข่ายก่อนและใช้แคชสำรอง | ดึงข้อมูลล่าสุดจากเครือข่าย และใช้แคชสำรองเมื่อออฟไลน์ | | **บันเดิล Next.js** (`/_next/`) | ใช้เครือข่ายก่อนพร้อมอัปเดตแคช | ดึงข้อมูลจากเครือข่ายและอัปเดตแคช หากออฟไลน์จะใช้เวอร์ชันที่แคชไว้ | | **คำขอนำทาง** | ข้าม (ไม่ดักจับคำขอ) | เบราว์เซอร์จัดการการสลับสำรองจาก HTTP/3→HTTP/2 ซ็อกเก็ต QUIC ที่ใช้งานไม่ได้ต้องไม่กลายเป็น `Response.error()` | | **API / เส้นทางแดชบอร์ด** (`/api/`, `/a2a`, `/dashboard`) | ข้าม (ไม่แคช) | ส่งตรงไปยังเซิร์ฟเวอร์เสมอ — service worker จะไม่ดักจับคำขอ | ### หน้าออฟไลน์ เมื่อเครือข่ายไม่พร้อมใช้งานและผู้ใช้ไปยังหน้าใหม่ service worker จะแสดงหน้า `/offline` โดยเฉพาะ ซึ่ง: - แสดงข้อความ **"ปัญหาการเชื่อมต่อ"** ที่ชัดเจน - แสดง **ตัวบ่งชี้สถานะออนไลน์/ออฟไลน์** แบบสดที่อัปเดตตามเวลาจริง - มีปุ่ม **"ลองเชื่อมต่ออีกครั้ง"** เพื่อโหลดหน้าใหม่เมื่อการเชื่อมต่อกลับมา - เชื่อมโยงไปยัง **หน้าสถานะ** เพื่อการวินิจฉัย ### ไอคอนแอป OmniRoute มีไอคอนที่ปรับให้เหมาะสมกับแต่ละแพลตฟอร์ม: | ไฟล์ | ขนาด | ใช้โดย | | ---------------------- | ------------------ | --------------------------------------------- | | `icon-512.png` | 512×512 | ข้อความแจ้งติดตั้งของ Android, หน้าจอเริ่มต้น | | `apple-touch-icon.png` | 180×180 | ไอคอนหน้าจอหลักของ iOS | | `icon-192.svg` | 192×192 (เวกเตอร์) | ไอคอนแบบปรับเปลี่ยนได้ของ Android | | `apple-touch-icon.svg` | 180×180 (เวกเตอร์) | ตัวเลือกสำรองสำหรับ Apple | | `favicon.svg` | เวกเตอร์ | แท็บเบราว์เซอร์ | | `favicon.ico` | หลายขนาด | เบราว์เซอร์รุ่นเก่า | ### การลงทะเบียนอัตโนมัติ service worker จะได้รับการลงทะเบียนโดยอัตโนมัติผ่านคอมโพเนนต์ `` ในเลย์เอาต์ราก ผู้ใช้ไม่จำเป็นต้องดำเนินการใด ๆ — แอปจะพร้อมให้ติดตั้งทันทีที่เบราว์เซอร์ตรวจพบ manifest และ service worker ที่ถูกต้อง ## สถาปัตยกรรมทางเทคนิค ### Web App Manifest (`manifest.webmanifest`) สร้างโดย Next.js ผ่าน `src/app/manifest.ts`: ```json { "name": "OmniRoute", "short_name": "OmniRoute", "description": "OmniRoute คือเกตเวย์ AI สำหรับ LLM จากผู้ให้บริการหลายราย ใช้เอนด์พอยต์เดียวสำหรับผู้ให้บริการ AI ทั้งหมดของคุณ", "start_url": "/", "scope": "/", "display": "fullscreen", "orientation": "any", "background_color": "#0b0f1a", "theme_color": "#0b0f1a", "icons": [ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }, { "src": "/apple-touch-icon.png", "sizes": "180x180", "type": "image/png" } ] } ``` ### Service Worker (`public/sw.js`) Service Worker แบบพื้นฐาน (ไม่มีการพึ่งพาเฟรมเวิร์ก) ซึ่งประกอบด้วย: - **ขั้นตอนการติดตั้ง**: แคชล่วงหน้าสำหรับเชลล์ของแอป (หน้าหลัก หน้าออฟไลน์ Manifest และไอคอน) - **ขั้นตอนการเปิดใช้งาน**: ล้างแคชเวอร์ชันเก่าและเข้าควบคุมไคลเอนต์ทั้งหมด - **ขั้นตอนการดึงข้อมูล**: กำหนดเส้นทางอย่างชาญฉลาดตามประเภทคำขอ (การนำทาง แอสเซตแบบคงที่ และ API) - **การกำหนดเวอร์ชันแคช**: `omniroute-pwa-v3` — เพิ่มเวอร์ชันนี้เพื่อบังคับให้สร้างแคชใหม่เมื่อมีการอัปเดต ### Metadata ของเลย์เอาต์ (`src/app/layout.tsx`) เลย์เอาต์รากมีแท็ก Meta ทั้งหมดที่จำเป็นสำหรับการเป็นไปตามข้อกำหนดของ PWA: - ลิงก์ `manifest` ไปยัง `/manifest.webmanifest` - `apple-web-app-capable: true` สำหรับโหมดสแตนด์อโลนบน iOS - `apple-web-app-status-bar-style: black-translucent` - `mobile-web-app-capable: yes` สำหรับ Chrome บน Android - `theme-color: #0b0f1a` - `viewport-fit: cover` สำหรับการเรนเดอร์แบบเต็มขอบจอ ### คอมโพเนนต์: `PwaRegister` อยู่ที่ `src/shared/components/PwaRegister.tsx` โดยคอมโพเนนต์ฝั่งไคลเอนต์นี้จะ: 1. ทำงานเมื่อเมานต์ (เฉพาะฝั่งไคลเอนต์) 2. ตรวจสอบว่าเบราว์เซอร์รองรับ `serviceWorker` หรือไม่ 3. ลงทะเบียน `/sw.js` แบบไม่มีการแจ้งเตือน (ข้อผิดพลาดจะถูกละเว้นเพื่อไม่ให้ขัดขวางการทำงานของแอป) 4. ไม่เรนเดอร์สิ่งใด (`return null`) — เป็นคอมโพเนนต์ที่ใช้สำหรับผลข้างเคียงเท่านั้น ## การใช้งานร่วมกับ Termux (Android) เมื่อเรียกใช้ OmniRoute บน Android ผ่าน Termux ตัว PWA จะทำงานร่วมกันได้อย่างราบรื่น: 1. เริ่ม OmniRoute ใน Termux: `npx omniroute` 2. เปิด Chrome บนโทรศัพท์เครื่องเดียวกัน: `http://localhost:20128` 3. ติดตั้ง PWA ผ่าน "เพิ่มไปยังหน้าจอหลัก" 4. PWA จะเชื่อมต่อกับเซิร์ฟเวอร์ Termux ภายในเครื่อง — ทุกอย่างทำงานบนอุปกรณ์ การใช้งานร่วมกันในลักษณะนี้หมายความว่าโทรศัพท์ Android ของคุณเป็นทั้ง **เซิร์ฟเวอร์** (Termux) และ **ไคลเอนต์** (PWA) — ทำให้เป็นเกตเวย์ AI แบบครบวงจรที่ทำงานได้ด้วยตัวเอง ## การใช้งานจากอุปกรณ์อื่น ติดตั้ง PWA บนอุปกรณ์ใดก็ได้ที่สามารถเข้าถึงเซิร์ฟเวอร์ OmniRoute ของคุณผ่านเบราว์เซอร์: - **โทรศัพท์/แท็บเล็ตเครื่องอื่น**: ไปที่ `http://PHONE_IP:20128` และติดตั้ง PWA - **แล็ปท็อป**: เปิด Chrome/Edge และติดตั้งเป็น PWA สำหรับเดสก์ท็อป - **สมาร์ตทีวีที่มีเบราว์เซอร์**: เข้าถึงแดชบอร์ดแบบเต็มหน้าจอ ## การปรับแต่ง ### ชื่ออินสแตนซ์ ชื่อของ PWA จะใช้ตามการตั้งค่า **ชื่ออินสแตนซ์** จาก `แดชบอร์ด → การตั้งค่า` หากคุณเปลี่ยนชื่ออินสแตนซ์เป็น "เกตเวย์ AI ของฉัน" PWA ที่ติดตั้งแล้วจะแสดงชื่อนั้น ### Favicon แบบกำหนดเอง หากคุณอัปโหลด Favicon แบบกำหนดเองผ่าน `แดชบอร์ด → การตั้งค่า` ไอคอน PWA บนเดสก์ท็อปจะแสดงไอคอนที่กำหนดเอง ส่วนไอคอนบนหน้าจอหลักของอุปกรณ์มือถือจะใช้ไฟล์ `icon-512.png` และ `apple-touch-icon.png` ที่สร้างไว้ล่วงหน้า ## ข้อจำกัด - **ไม่มีการแจ้งเตือนแบบพุช** — Service worker ไม่ได้ใช้งาน Push API โดยการแจ้งเตือนจะได้รับการจัดการโดยแอป Electron แทน - **ไม่มีการซิงค์เบื้องหลัง** — การดำเนินการแบบออฟไลน์จะไม่ถูกจัดคิวไว้เพื่อดำเนินการซ้ำ PWA นี้มีจุดประสงค์หลักเพื่อใช้ดูแดชบอร์ด - **ข้อจำกัดของ iOS** — Safari บน iOS ไม่รองรับฟีเจอร์ PWA ทั้งหมด (เช่น การแจ้งให้ติดตั้งต้องดำเนินการด้วยตนเอง และ Service worker เบื้องหลังมีข้อจำกัด) - **ขนาดแคช** — Service worker จะแคชเฉพาะแอสเซตแบบสแตติกเท่านั้น ส่วนเพย์โหลดการตอบกลับขนาดใหญ่จากเส้นทาง `/api/` จะไม่ถูกแคช - **ไอคอนแบบกำหนดเองบนอุปกรณ์เคลื่อนที่** — การเปลี่ยน favicon ในการตั้งค่าจะไม่อัปเดตไอคอนบนหน้าจอหลักของอุปกรณ์เคลื่อนที่ (จำเป็นต้องสร้างไอคอน PWA ใหม่) ## เอกสารอ้างอิงไฟล์ | ไฟล์ | วัตถุประสงค์ | | --------------------------------------- | -------------------------------------------------------------- | | `src/app/manifest.ts` | เส้นทาง Manifest ของ Next.js (สร้าง `manifest.webmanifest`) | | `public/sw.js` | Service worker ที่มีตรรกะการแคช | | `src/shared/components/PwaRegister.tsx` | คอมโพเนนต์ไคลเอนต์ที่ลงทะเบียน Service worker | | `src/app/offline/page.tsx` | หน้าสำรองเมื่อออฟไลน์พร้อมตัวบ่งชี้สถานะแบบเรียลไทม์ | | `src/app/layout.tsx` | เลย์เอาต์รูทพร้อมเมทาดาทา PWA (apple-web-app, theme-color ฯลฯ) | | `public/icon-512.png` | ไอคอน PNG ขนาด 512×512 (Android, หน้าจอเริ่มต้น) | | `public/apple-touch-icon.png` | ไอคอน PNG ขนาด 180×180 (หน้าจอหลักของ iOS) | | `public/icon-192.svg` | ไอคอน SVG ขนาด 192×192 (ไอคอนแบบปรับได้ของ Android) | | `public/apple-touch-icon.svg` | ไอคอน SVG ขนาด 180×180 (ไอคอนสำรองสำหรับ Apple) |