ACTUAL CODEX CLI RUN / STEP 04 Codex CLI 0.153.4 · captured 11 September 2026 This is a selected event transcript, not the Codex Desktop UI. Reasoning, environment diagnostics and supporting skill instructions are omitted. Paths are shortened. COMMAND codex exec -C --sandbox workspace-write --json --output-last-message 04-final.txt - < prompts/04-interaction.txt PROMPT SENT อ่าน inputs.md, scope.txt, index.html และ style.css เพิ่ม progressive enhancement โดยสร้าง main.js และแก้ HTML/CSS เฉพาะที่จำเป็น 1) มีปุ่ม #theme-toggle: defaultLight, toggleDark/Light, data-themeบนhtml, aria-pressed=trueเมื่อDark, ข้อความบอกโหมด/การกระทำชัดเจน. จำเฉพาะค่าที่ผู้ใช้เลือกใน localStorage key psu-course-website-theme-v1. storageถูกปิดต้องยังใช้ได้ด้วยtry/catch. ไม่มีค่าที่บันทึกแล้วให้Lightแม้OSDark 2) ค้นหาบทเรียน #lesson-search มีlabelจริง: trimช่องว่าง ใช้ชื่อ+คำอธิบายของ11บท matchอย่างปลอดภัย (ไม่ใช้innerHTMLจากผู้ใช้) 3) filter buttons data-filter=all/day1/day2 มีaria-pressedที่ตรง, ค้นหากับfilterทำงานร่วมกันบน article[data-lesson][data-day]. วัน1บท00–05, วัน2บท06–10 4) #lesson-status role=status aria-live=politeแจ้งจำนวนที่พบ, #lesson-empty แสดงเมื่อ0ผล, #clear-search คืนqueryว่างและfilterall แล้วfocussearch ไม่ทำให้focusกระโดดขณะพิมพ์ 5) controlsค้นหา/filter/themeเริ่มมีhiddenในHTMLและแสดงเฉพาะเมื่อJSพร้อม ถ้าJSปิดยังเห็น11บทและเข้าเรียนด้วยanchorได้. ให้CSSรักษาความหมาย [hidden] { display:none !important } ไม่ต้องเพิ่มprogress tracking analytics forms API framework หรือdependencies. ไม่มีcredentials/login. reducedmotionและprintยังทำงาน. เก็บ acceptance-notes.md ระบุการตรวจที่ทำได้จริงและสิ่งที่ยังรอbrowser ไม่อ้างรันbrowserถ้ายังไม่ได้รัน EXECUTED COMMAND /bin/bash -lc "pwd && rg --files -g 'AGENTS.md' -g 'inputs.md' -g 'scope.txt' -g 'index.html' -g 'style.css' -g 'acceptance-notes.md' -g 'package.json' -g 'README*'" OUTPUT style.css index.html inputs.md scope.txt EXIT 0 EXECUTED COMMAND /bin/bash -lc "sed -n '1,240p' inputs.md && sed -n '1,260p' scope.txt && sed -n '1,360p' index.html && sed -n '1,420p' style.css" OUTPUT # ข้อมูลตั้งต้นสำหรับเว็บไซต์ตัวอย่างรายวิชา S1 — ข้อมูลที่ผู้ใช้ให้: 977-121 Module: Website Design and Development, มหาวิทยาลัยสงขลานครินทร์ วิทยาเขตภูเก็ต / Prince of Songkla University, Phuket Campus. S2 — design-system/course.json และ docs/instructor/course-guide.md ของหลักสูตรนี้: AI Web Studio — ออกแบบและพัฒนาเว็บไซต์ด้วย AI เป็นหน่วย workshop 2 วัน × 4 ชั่วโมง = 8 ชั่วโมง รวมพัก 10 นาทีต่อวัน เป็นส่วนหนึ่งของรายวิชา ไม่ใช่ชั่วโมง/หน่วยกิต/ประมวลรายวิชาฉบับมหาวิทยาลัยทั้งหมด S3 — DESIGN.md และ Rawinnipa Brand Guidelines v1.0 ที่ใช้กับ course shell: Inter + Noto Sans Thai, light default: #F7F7F7 พื้น, #0A0A0A ข้อความ, #3EDC81 accent มีข้อความเข้ม, #17653F สำหรับข้อความเขียวบนพื้นอ่อน. Dark: #0A0A0A canvas, #171717 surface, #F7F7F7 text. จำกัด green เป็นจุดเน้น; focus มองเห็น; print กระดาษขาว. ลิงก์ผู้ออกแบบ/ผู้จัดทำสื่อ https://rawinsoft.com/ ไม่อ้าง partnership หรือเจ้าของมหาวิทยาลัยเพิ่ม. ห้ามสร้างตราหรือโลโก้มหาวิทยาลัยขึ้นเอง S4 — บทเรียนที่มีจริงใน repository (ชื่อย่อใช้แสดงได้): - 00 | บทเรียน 00 — เตรียมเครื่องและเข้าใจว่าเว็บทำงานอย่างไร | /learn/lessons/00-setup-and-web-basics/ - 01 | บทที่ 1: พื้นฐาน Vibe Coding สำหรับเว็บที่ส่งมอบได้จริง | /learn/lessons/01-vibe-coding-foundations/ - 02 | บทที่ 2: Agent Skills, MCP และ Plugins โดยไม่ทำให้บริบทพัง | /learn/lessons/02-agent-skills/ - 03 | บทที่ 3: Specs และ Prompts สำหรับเว็บบริษัท, Catalog และเว็บ animation | /learn/lessons/03-specs-and-prompts/ - 04 | บทเรียน 04 — ออกแบบ, ภาพ และคุณภาพก่อนเขียนหน้าเว็บ | /learn/lessons/04-design-and-assets/ - 05 | บทเรียน 05 — เว็บบริษัท: contact form ที่รันกับ demo นี้ได้จริง | /learn/lessons/05-company-website/ - 06 | บทเรียน 06 — เว็บเช่าอุปกรณ์: availability และ capacity ของ demo ที่รันได้จริง | /learn/lessons/06-rental-and-data/ - 07 | บทเรียน 07 — GSAP product story, storefront และ quote demo | /learn/lessons/07-gsap-storefront/ - 08 | บทเรียน 08 — รันและตรวจ deployment path ของโปรเจกต์ Cloudflare นี้ | /learn/lessons/08-cloudflare-deploy/ - 09 | บทเรียน 09 — Production Operations: security, observability, rollback และต้นทุน | /learn/lessons/09-production-operations/ - 10 | บทเรียน 10 — สร้างเว็บของตนเองและส่งมอบอย่างตรวจสอบได้ | /learn/lessons/10-capstone-and-handoff/ S5 — ปลายทางที่มีจริง: / (เว็บไซต์สื่อหลักสูตร), /resources/, /handbook/, /learn/labs/install-ai-tools/, /learn/labs/research-to-brand-website/, /learn/labs/design-tools-and-mcp/. เว็บไซต์ตัวอย่างจะอยู่ /examples/course-website/. ลิงก์อ่านการสร้างทีละขั้นใช้ /learn/labs/build-course-website-step-by-step/ (กำลังสร้าง) S6 — สินทรัพย์ font ที่ใช้ได้: /fonts/Inter-Regular.woff2, /fonts/Inter-Bold.woff2, /fonts/NotoSansThai-Regular.woff2, /fonts/NotoSansThai-Bold.woff2. ใช้ตัวอักษร/HTML/SVG ธรรมดา ไม่ต้องสร้างภาพ raster หรือโหลด font ภายนอก GAP — ยังไม่มีเจ้าของยืนยันชื่อผู้สอน จำนวนหน่วยกิต ตารางภาคเรียน เกณฑ์ให้คะแนน ช่องทางสมัคร หรือบัญชี LMS. ไม่สร้างข้อมูลเหล่านี้ ไม่สร้าง login/รับสมัครที่ทำงานปลอม PROPOSAL — กลุ่มเป้าหมายของ prototype คือผู้เรียนที่ต้องการรู้ว่าเริ่มตรงไหน เลือกบทเรียน และดูแผน workshop. เว็บไซต์นี้เป็นตัวอย่างประกอบการเรียน ไม่ใช่ระบบมหาวิทยาลัยอย่างเป็นทางการ ขอบเขตงาน - เว็บภาษาไทย static HTML/CSS/JS หน้าเดียว; API/ฐานข้อมูลไม่จำเป็นสำหรับเนื้อหาสาธารณะ - ให้ไปถึงบทเรียนได้จริง; optional ค้นหาบทเรียนและกรอง Day1/Day2/ทั้งหมด - โปรเจ็กต์เดิมมีเวลา workshop 8h; เนื้อหาเกินเวลาเป็นอ่านต่อ. แสดง Day1/Day2 สรุปไม่แต่งตารางรายวิชาเต็ม - สร้างแบบทีละขั้น: brief → semantic HTML → responsive CSS → JS interaction → QA → publication - code ทั้งหมดและผลแต่ละช่วงต้องตรวจ/ย้อนดูได้ ข้อมูลยืนยันเพิ่มเติมหลังทบทวน brief (ก่อนขั้น02): แบ่ง Day1 เป็นบท00–05, Day2 เป็นบท06–10 ตาม dayData ใน scripts/build.mjs. การแบ่งนี้เป็นเส้นทางคลังบทเรียน; ตารางในห้องยังเลือกกิจกรรมตามเวลา4ชม/วัน ไม่ได้หมายถึงต้องทำครบทุกเนื้อหา แหล่งอ้างอิงชื่อรายวิชาภายนอก: https://computing.psu.ac.th/th/b-eng-digital-engineer-international-program/ ตรวจ11กันยายน2026. ใช้เฉพาะยืนยันชื่อวิชา/บริบทPSUPhuket; ไม่เอาจำนวนหน่วยกิตในแผนคนละรุ่นมาใส่เป็นเวลาworkshop Work only in this isolated directory. No network, servers, dependencies, auth, deployment or files outside this directory. Use the supplied inputs. Write only explicitly requested files. Do not read global config or credentials. Do not spawn agents. Your work will be reviewed and published by the parent agent. Keep the final reply brief in Thai, naming created files and remaining gaps. 977-121 Module: Website Design and Development | AI Web Studio

AI Web Studio

977-121 Module: Website Design and Development

ออกแบบและพัฒนาเว็บไซต์ด้วย AI

เส้นทางเรียนรู้เพื่อเริ่มสร้างเว็บไซต์ด้วย AI ตั้งแต่เครื่องมือและพื้นฐาน ไปจนถึงการส่งมอบที่ตรวจสอบได้

เริ่มจากบทเรียน 00 ดูบทเรียนทั้งหมด

ภาพรวมเส้นทางการทำงาน

  1. Brief
  2. Build
  3. Verify
  4. Publish

เป้าหมายของ workshop

AI Web Studio เป็นส่วนหนึ่งของรายวิชา เพื่อช่วยผู้เรียนเลือกจุดเริ่มต้น เข้าถึงบทเรียนจริง และฝึกสร้างเว็บไซต์ด้วย AI อย่างมีขั้นตอน

  • เข้าใจเครื่องมือและพื้นฐานของเว็บก่อนเริ่มลงมือ
  • ใช้ specs, prompts และเครื่องมือ AI เพื่อพัฒนาเว็บไซต์
  • ตรวจคุณภาพและส่งมอบงานที่ตรวจสอบได้

workshop นี้ไม่ใช่รายละเอียดรายวิชาฉบับมหาวิทยาลัยหรือระบบมหาวิทยาลัยอย่างเป็นทางการ

แผน AI Web Studio 2 วัน

รวม 8 ชั่วโมง: 2 วัน วันละ 4 ชั่วโมง และพัก 10 นาทีต่อวัน

แผนนี้เป็นเส้นทางเลือกใช้คลังบทเรียนตามเวลาจริงในห้องเรียน ไม่ได้สัญญาว่าจะสอนครบทุกบทภายใน 8 ชั่วโมง

Day 1 — เริ่มต้น ออกแบบ และสร้างฐานงาน

4 ชั่วโมง รวมพัก 10 นาที: เลือกกิจกรรมจากบท 00–05 เพื่อเตรียมเครื่องมือ วางบริบท และเริ่มทำเว็บไซต์

Day 2 — ขยายงาน ตรวจสอบ และส่งมอบ

4 ชั่วโมง รวมพัก 10 นาที: เลือกกิจกรรมจากบท 06–10 เพื่อทำงานกับข้อมูล ประสบการณ์เว็บ การ deploy และการส่งมอบ

เส้นทางบทเรียน

บท 00–05 อยู่ในเส้นทาง Day 1 และบท 06–10 อยู่ในเส้นทาง Day 2 เพื่อใช้เลือกเนื้อหาใน workshop

Day 1: บท 00–05

00 — เตรียมเครื่องและเข้าใจว่าเว็บทำงานอย่างไร

เตรียมเครื่องมือและทำความเข้าใจพื้นฐานการทำงานของเว็บก่อนเริ่มสร้างงาน

อ่านบทเรียน 00

01 — พื้นฐาน Vibe Coding สำหรับเว็บที่ส่งมอบได้จริง

เรียนรู้พื้นฐาน Vibe Coding เพื่อสร้างเว็บที่พร้อมส่งมอบ

อ่านบทเรียน 01

02 — Agent Skills, MCP และ Plugins โดยไม่ทำให้บริบทพัง

ทำความเข้าใจ Agent Skills, MCP และ Plugins พร้อมดูแลบริบทการทำงาน

อ่านบทเรียน 02

03 — Specs และ Prompts สำหรับเว็บบริษัท, Catalog และเว็บ animation

เขียน specs และ prompts สำหรับเว็บบริษัท catalog และเว็บที่มี animation

อ่านบทเรียน 03

04 — ออกแบบ, ภาพ และคุณภาพก่อนเขียนหน้าเว็บ

วางงานออกแบบ ภาพ และเกณฑ์คุณภาพก่อนเริ่มเขียนหน้าเว็บ

อ่านบทเรียน 04

05 — เว็บบริษัท: contact form ที่รันกับ demo นี้ได้จริง

สร้างเว็บบริษัทและ contact form ที่ทำงานกับ demo ได้จริง

อ่านบทเรียน 05

Day 2: บท 06–10

06 — เว็บเช่าอุปกรณ์: availability และ capacity ของ demo ที่รันได้จริง

พัฒนาเว็บเช่าอุปกรณ์ที่จัดการ availability และ capacity ของ demo ได้

อ่านบทเรียน 06

08 — รันและตรวจ deployment path ของโปรเจกต์ Cloudflare นี้

รันโปรเจกต์และตรวจเส้นทาง deployment ของ Cloudflare

อ่านบทเรียน 08

09 — Production Operations: security, observability, rollback และต้นทุน

ดูแล production ทั้งความปลอดภัย การสังเกตการณ์ การ rollback และต้นทุน

อ่านบทเรียน 09

10 — สร้างเว็บของตนเองและส่งมอบอย่างตรวจสอบได้

สร้างเว็บของตนเองและเตรียมการส่งมอบที่ตรวจสอบได้

อ่านบทเรียน 10

Labs และแหล่งเรียนต่อ

977-121 Module: Website Design and Development · มหาวิทยาลัยสงขลานครินทร์ วิทยาเขตภูเก็ต

สื่อออกแบบและจัดทำโดย Rawinnipa

@font-face { font-family: "Inter"; src: url("/fonts/Inter-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: "Inter"; src: url("/fonts/Inter-Bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; } @font-face { font-family: "Noto Sans Thai"; src: url("/fonts/NotoSansThai-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: "Noto Sans Thai"; src: url("/fonts/NotoSansThai-Bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; } :root { color-scheme: light; --surface: #f7f7f7; --surface-raised: #ffffff; --text: #0a0a0a; --muted: #505050; --line: #d0d0d0; --action: #3edc81; --action-text: #063119; --link: #17653f; --focus: #17653f; --radius: 0.75rem; --page-width: 72rem; } html[data-theme="dark"] { color-scheme: dark; --surface: #0a0a0a; --surface-raised: #171717; --text: #f7f7f7; --muted: #c8c8c8; --line: #555555; --action: #3edc81; --action-text: #062c16; --link: #82efae; --focus: #82efae; } * { box-sizing: border-box; } html { background: var(--surface); } body { margin: 0; min-width: 0; background: var(--surface); color: var(--text); font-family: Inter, "Noto Sans Thai", sans-serif; font-size: 1rem; line-height: 1.7; overflow-wrap: anywhere; } a { color: var(--link); text-decoration-thickness: 0.12em; text-underline-offset: 0.18em; } a:hover { color: var(--text); } a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; } .skip-link { position: fixed; z-index: 5; top: 0.75rem; left: 0.75rem; padding: 0.6rem 0.85rem; background: var(--action); color: var(--action-text); transform: translateY(-150%); } .skip-link:focus { transform: translateY(0); } .site-header { border-bottom: 1px solid var(--line); } .header-inner, .page-shell, .site-footer { width: min(100% - 2rem, var(--page-width)); margin-inline: auto; } .header-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 2rem; padding-block: 1.15rem; } .course-identity p { margin: 0; } .eyebrow, .preview-label { margin: 0; color: var(--muted); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; } .course-code { font-weight: 700; } .institution { color: var(--muted); font-size: 0.9rem; } .site-nav { display: flex; flex: 1 1 26rem; flex-wrap: wrap; justify-content: flex-end; gap: 0.4rem 1rem; } .site-nav a { padding: 0.35rem 0; white-space: nowrap; } .page-shell { padding-block: clamp(2.5rem, 6vw, 5.5rem); } .course-hero { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(15rem, 0.65fr); gap: clamp(1.5rem, 5vw, 5rem); align-items: end; padding-bottom: clamp(2.5rem, 5vw, 4rem); border-bottom: 1px solid var(--line); } h1, h2, h3, h4 { margin: 0; color: var(--text); line-height: 1.22; text-wrap: balance; } h1 { max-width: 15ch; font-size: clamp(2.3rem, 7vw, 5.6rem); letter-spacing: -0.055em; } h2 { font-size: clamp(1.65rem, 4vw, 2.7rem); letter-spacing: -0.035em; } h3 { font-size: clamp(1.2rem, 2.2vw, 1.55rem); } h4 { font-size: 1.08rem; } .hero-subtitle { margin: 1.15rem 0 0; font-size: clamp(1.15rem, 2vw, 1.5rem); font-weight: 700; } .hero-summary { max-width: 58ch; margin: 0.85rem 0 0; color: var(--muted); font-size: 1.08rem; } .hero-actions { display: flex; flex-wrap: wrap; gap: 0.75rem 1.25rem; margin: 1.75rem 0 0; align-items: center; } .hero-actions a:first-child { display: inline-flex; min-height: 2.8rem; align-items: center; padding: 0.55rem 1rem; background: var(--action); border: 1px solid var(--action); border-radius: var(--radius); color: var(--action-text); font-weight: 700; text-decoration: none; } .learning-preview { padding: clamp(1.25rem, 3vw, 2rem); border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-raised); } .learning-preview ol { display: grid; gap: 0.75rem; margin: 1rem 0 0; padding: 0; list-style: none; counter-reset: route; } .learning-preview li { display: flex; align-items: center; gap: 0.7rem; font-weight: 700; } .learning-preview li::before { display: grid; width: 1.8rem; height: 1.8rem; place-items: center; border: 1px solid var(--line); border-radius: 50%; color: var(--muted); content: counter(route, decimal-leading-zero); counter-increment: route; font: 700 0.7rem/1 Inter, sans-serif; } .content-section { padding-top: clamp(3rem, 8vw, 7rem); } .content-section > p, .goals-section li { max-width: 72ch; } .content-section > p { color: var(--muted); } .goals-section ul { padding-left: 1.25rem; } .day-grid, .lesson-grid { display: grid; gap: 1rem; margin-top: 1.5rem; } .day-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .day-card, [data-lesson] { min-width: 0; padding: clamp(1.1rem, 3vw, 1.6rem); border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-raised); } .day-card p, [data-lesson] p { color: var(--muted); } .lesson-day + .lesson-day { margin-top: 2.5rem; } .lesson-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } [data-lesson] { display: flex; flex-direction: column; gap: 0.85rem; } [data-lesson] p { margin: 0; } [data-lesson] a { width: fit-content; margin-top: auto; font-weight: 700; } .resources-section ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem 2rem; padding: 0; list-style: none; } .resources-section a { display: inline-block; padding: 0.35rem 0; } .site-footer { margin-top: 2rem; padding-block: 2rem 3rem; border-top: 1px solid var(--line); color: var(--muted); font-size: 0.92rem; } .site-footer p { margin: 0.25rem 0; } button, input, select, textarea { min-height: 2.75rem; border: 1px solid var(--line); border-radius: 0.4rem; background: var(--surface-raised); color: var(--text); font: inherit; } button, select { padding-inline: 0.8rem; } input, textarea { width: 100%; padding: 0.6rem 0.8rem; } @media (max-width: 46rem) { .header-inner, .site-nav { align-items: flex-start; justify-content: flex-start; } .course-hero, .day-grid, .lesson-grid, .resources-section ul { grid-template-columns: 1fr; } h1 { max-width: none; } .site-nav { flex-basis: 100%; } } @media print { :root, html[data-theme="dark"] { color-scheme: light; --surface: #fff; --surface-raised: #fff; --text: #000; --muted: #333; --line: #888; --link: #000; } .skip-link, .site-nav { display: none; } .page-shell { padding-block: 1.5rem; } .course-hero, .day-grid, .lesson-grid, .resources-section ul { grid-template-columns: 1fr; } a { color: #000; } a[href]::after { content: " (" attr(href) ")"; font-size: 0.8em; } } EXIT 0 CODEX MESSAGE ตรวจพบว่าโครงสร้างหน้าเดิมมีบทเรียนครบ 11 บทและรองรับธีมผ่าน CSS variables อยู่แล้ว จึงจะเติมเฉพาะ markup ของ controls ที่ซ่อนไว้เป็นค่าเริ่มต้น, JavaScript ที่ไม่พึ่ง dependency, และ style เฉพาะส่วนควบคุม โดยไม่แตะเนื้อหาบทเรียนหรือลิงก์ปลายทางครับ FILE CHANGES [ { "path": "/acceptance-notes.md", "kind": "add" }, { "path": "/index.html", "kind": "update" }, { "path": "/main.js", "kind": "add" }, { "path": "/style.css", "kind": "update" } ] EXECUTED COMMAND /bin/bash -lc "node --check main.js && rg -n 'data-theme=\"light\"|theme-toggle|lesson-search|data-filter|lesson-status|lesson-empty|main\\.js|\\[hidden\\]' index.html style.css main.js && printf '\\nLesson cards: ' && rg -c '
index.html:27: index.html:96: index.html:97: index.html:101:

index.html:103: index.html:190: Lesson cards: 11 Day 1 cards: 6 Day 2 cards: 5 EXIT 0 CODEX MESSAGE ผลการตรวจแบบ static ผ่านแล้ว: `main.js` ผ่าน syntax check, พบ lesson cards 11 ใบ (Day 1 = 6, Day 2 = 5) และ diff ไม่มี whitespace error. ยังไม่ได้เปิดเบราว์เซอร์ จึงบันทึกไว้ตรงไปตรงมาใน acceptance notes ว่าส่วน interaction/runtime รอการตรวจจริงครับ EXECUTED COMMAND /bin/bash -lc "git status --short && sed -n '1,240p' main.js && sed -n '1,180p' acceptance-notes.md" OUTPUT ?? ../../../.diagram-design ?? ../../../.gitattributes ?? ../../../.gitignore ?? ../../../CLAUDE.md ?? ../../../DESIGN.md ?? ../../../INFRASTRUCTURE.md ?? ../../../PROJECT_PLAN.md ?? ../../../README.md ?? ../../ ?? ../../../design-system/ ?? ../../../diagrams/ ?? ../../../docs/ ?? ../../../examples/ ?? ../../../migrations/ ?? ../../../package-lock.json ?? ../../../package.json ?? ../../../playwright.config.mjs ?? ../../../public/ ?? ../../../scripts/ ?? ../../../src/ ?? ../../../tests/ ?? ../../../training-skills/ ?? ../../../tsconfig.json ?? ../../../worker-configuration.d.ts ?? ../../../wrangler.jsonc (() => { "use strict"; const storageKey = "psu-course-website-theme-v1"; const root = document.documentElement; const themeToggle = document.querySelector("#theme-toggle"); const controls = document.querySelector(".lesson-controls"); const search = document.querySelector("#lesson-search"); const filterButtons = [...document.querySelectorAll("[data-filter]")]; const clearSearch = document.querySelector("#clear-search"); const status = document.querySelector("#lesson-status"); const empty = document.querySelector("#lesson-empty"); const lessons = [...document.querySelectorAll("article[data-lesson][data-day]")]; const lessonDays = [...document.querySelectorAll(".lesson-day")]; let theme = "light"; let selectedFilter = "all"; try { const savedTheme = window.localStorage.getItem(storageKey); if (savedTheme === "light" || savedTheme === "dark") { theme = savedTheme; } } catch (_) { // Theme selection still works for this visit when storage is unavailable. } function setTheme(nextTheme) { theme = nextTheme; root.dataset.theme = theme; const isDark = theme === "dark"; themeToggle.setAttribute("aria-pressed", String(isDark)); themeToggle.textContent = isDark ? "เปลี่ยนเป็นโหมดสว่าง" : "เปลี่ยนเป็นโหมดมืด"; themeToggle.setAttribute( "aria-label", isDark ? "โหมดปัจจุบัน: มืด เปลี่ยนเป็นโหมดสว่าง" : "โหมดปัจจุบัน: สว่าง เปลี่ยนเป็นโหมดมืด" ); } function normalise(value) { return value.replace(/\s+/g, " ").trim().toLocaleLowerCase(); } function lessonText(lesson) { const title = lesson.querySelector("h4"); const description = lesson.querySelector("p"); return normalise(`${title?.textContent ?? ""} ${description?.textContent ?? ""}`); } function updateLessons() { const query = normalise(search.value); let count = 0; lessons.forEach((lesson) => { const matchesFilter = selectedFilter === "all" || lesson.dataset.day === selectedFilter; const matchesQuery = query === "" || lessonText(lesson).includes(query); const visible = matchesFilter && matchesQuery; lesson.hidden = !visible; if (visible) count += 1; }); lessonDays.forEach((day) => { day.hidden = !day.querySelector("article[data-lesson]:not([hidden])"); }); empty.hidden = count !== 0; status.textContent = `พบบทเรียน ${count} จาก ${lessons.length} บท`; } function setFilter(nextFilter) { selectedFilter = nextFilter; filterButtons.forEach((button) => { button.setAttribute("aria-pressed", String(button.dataset.filter === selectedFilter)); }); updateLessons(); } if (themeToggle) { themeToggle.hidden = false; setTheme(theme); themeToggle.addEventListener("click", () => { const nextTheme = theme === "dark" ? "light" : "dark"; setTheme(nextTheme); try { window.localStorage.setItem(storageKey, nextTheme); } catch (_) { // Storage can be disabled without preventing the visual toggle. } }); } if (controls && search && clearSearch && status && empty && lessons.length > 0) { controls.hidden = false; search.addEventListener("input", updateLessons); filterButtons.forEach((button) => { button.addEventListener("click", () => setFilter(button.dataset.filter)); }); clearSearch.addEventListener("click", () => { search.value = ""; setFilter("all"); search.focus(); }); updateLessons(); } })(); # Acceptance notes ## ตรวจที่ทำได้จริง - ตรวจ source ด้วย `rg` และการอ่านไฟล์ว่า HTML มีบทเรียน `article[data-lesson][data-day]` 11 บท: Day 1 คือ 00–05 (6 บท) และ Day 2 คือ 06–10 (5 บท) - ตรวจว่า controls ของ theme, search และ filter เริ่มต้นด้วย `hidden`; หาก JavaScript ไม่ทำงาน เนื้อหาบทเรียนและลิงก์ anchor ทั้ง 11 บทยังคงอยู่ใน HTML - ตรวจว่า `main.js` ใช้ `textContent` เพื่อนำชื่อและคำอธิบายมาค้นหา และไม่สร้าง HTML จากค่าค้นหาของผู้ใช้ - ตรวจ logic แบบ static ว่า default theme คือ light, `data-theme` อยู่บน `html`, `aria-pressed` เปลี่ยนตาม dark mode, และอ่าน/เขียน `psu-course-website-theme-v1` อยู่ใน `try/catch` - ตรวจว่า search trim ช่องว่าง, search/filter ประกอบกัน, clear คืนค่า query และ filter all แล้วจึง focus ช่องค้นหา - ตรวจ CSS ว่ามี `[hidden] { display: none !important; }` และ media print ยังบังคับสีสำหรับกระดาษพร้อมซ่อน controls ## สิ่งที่ยังรอการตรวจในเบราว์เซอร์ - ยังไม่ได้รันเบราว์เซอร์หรือ local server ในงานนี้ จึงยังไม่ได้ยืนยันการกดปุ่ม, การประกาศ live region, keyboard focus, localStorage ที่ถูกปิด, responsive layout, reduced-motion และ print preview จาก runtime จริง EXIT 0 CODEX MESSAGE เสร็จแล้ว - สร้าง [main.js](/main.js) สำหรับ theme toggle, ค้นหา, filter, status และ clear/focus - แก้ [index.html](/index.html) และ [style.css](/style.css) เพื่อ progressive enhancement และ `[hidden]` - สร้าง [acceptance-notes.md](/acceptance-notes.md) พร้อมหลักฐาน static checks และรายการที่ยังรอตรวจในเบราว์เซอร์ ตรวจแล้ว: `node --check main.js`, ตรวจจำนวนบทเรียน 11 บท (Day 1: 6, Day 2: 5), และ `git diff --check` ผ่าน. ยังไม่ได้รัน browser/local server จึงไม่ได้อ้างผล interaction runtime. FINAL RESPONSE เสร็จแล้ว - สร้าง [main.js](/main.js) สำหรับ theme toggle, ค้นหา, filter, status และ clear/focus - แก้ [index.html](/index.html) และ [style.css](/style.css) เพื่อ progressive enhancement และ `[hidden]` - สร้าง [acceptance-notes.md](/acceptance-notes.md) พร้อมหลักฐาน static checks และรายการที่ยังรอตรวจในเบราว์เซอร์ ตรวจแล้ว: `node --check main.js`, ตรวจจำนวนบทเรียน 11 บท (Day 1: 6, Day 2: 5), และ `git diff --check` ผ่าน. ยังไม่ได้รัน browser/local server จึงไม่ได้อ้างผล interaction runtime.