{"id":3068,"date":"2026-08-13T19:46:03","date_gmt":"2026-08-13T11:46:03","guid":{"rendered":"https:\/\/www.ukm.my\/wadahdigital\/?p=3068"},"modified":"2026-08-14T10:42:16","modified_gmt":"2026-08-14T02:42:16","slug":"merintis-masa-depan-peranan-ai-dalam-landskap-pengajaran-bahasa-di-ipt","status":"publish","type":"post","link":"https:\/\/www.ukm.my\/wadahdigital\/merintis-masa-depan-peranan-ai-dalam-landskap-pengajaran-bahasa-di-ipt\/","title":{"rendered":"MERINTIS MASA DEPAN: PERANAN AI DALAM LANDSKAP PENGAJARAN BAHASA DI IPT"},"content":{"rendered":"<p><center><strong>Maryam Mohamed Amin<\/strong><br \/>\nPusat Pengajian Citra Universiti (PPCU)<br \/>\n* Penulis Koresponden : maryam.amin@ukm.edu.my<\/center><\/p>\n<p><strong>Kata Kunci:<\/strong> Pendidikan Bahasa, Kecerdasan Buatan, Pedagogi Berasaskan Nilai, Pertuturan Masa Nyata, Notebook LM<\/p>\n<p><strong>Kecemerlangan dan Kecundangan AI<\/strong><\/p>\n<p>Kepesatan pembangunan Kecerdasan Buatan (AI) terus memukau dunia dengan kejutan yang luar biasa. Teknologi ini kini berupaya melakar pelbagai pencapaian besar daripada memenangi kes mahkamah dan membangunkan vaksin, sehinggalah merangka kertas kerja saintifik serta menterjemah teks dan video ke dalam pelbagai bahasa. Malah, keupayaan AI kini dilihat mampu mengambil alih peranan peguam, saintis, dan pakar linguistik. Artikel ini sendiri merupakan bukti kolaborasi tersebut, di mana draf asalnya dirangka dalam bahasa Inggeris. Sebagai seorang guru Bahasa Inggeris, bahasa berkenaan lebih dekat dengan hati penulis. Draf tersebut kemudiannya diterjemah dan diselaraskan mengikut spesifikasi templat Wadah Digital UKM ini ke dalam Bahasa Melayu standard DBP menggunakan aplikasi NotebookLM.<br \/>\n<center><br \/>\n<a href=\"https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture13.png\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture13-287x300.png\" alt=\"\" width=\"287\" height=\"300\" class=\"alignnone size-medium wp-image-3070\" srcset=\"https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture13-287x300.png 287w, https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture13.png 413w\" sizes=\"(max-width: 287px) 100vw, 287px\" \/><\/a><\/center><br \/>\n<center>Rajah 1: Peguam AI menang kes pertama (The Guardian, Jun 2026) <\/center><\/p>\n<p>Namun, di sebalik lonjakan teknologi ini, AI tidak terlepas daripada kelemahan bersiri terutamanya isu &#8220;halusinasi&#8221;, iaitu kecenderungan sistem menjana maklumat palsu dengan penuh keyakinan. Impak kepincangan ini bukan sekadar teori, buktinya, seorang pempengaruh sebelum ini terpaksa menarik balik penerbitan buku panduan kerohaniannya setelah mendapati penulis hantu AI (<em>AI ghostwriter<\/em>) telah memalsukan ayat-ayat suci al-Quran. Sementara itu dalam sektor kejuruteraan, sebuah syarikat pengeluar kenderaan terpaksa melantik semula jurutera manusia demi mengambil alih tugas penilaian keselamatan tegar yang gagal disempurnakan oleh teknologi tersebut. Malah, dunia akademik antarabangsa turut digemparkan dengan penarikan kajian sekumpulan individu dari Indonesia dalam sebuah persidangan perubatan terkemuka di Denmark, selepas data penyelidikan mereka dikesan direkayasa secara palsu menggunakan AI bagi mendapatkan geran perjalanan.<\/p>\n<p><strong>AI Tidak Boleh Menggantikan Pertuturan Masa Nyata<\/strong><\/p>\n<p>Tatkala landskap teknologi berkembang pantas, hala tuju pendidikan bahasa di peringkat pengajian tinggi sering mencetuskan perbincangan kritis. Perubahan drastik telah bermula apabila para pendidik bahasa mula mengetepikan format penulisan esei tradisional kepada penilaian berorientasikan kecekapan bahasa berpandukan standard CEFR (Common European Framework). Rentetan itu, fokus pentaksiran kini dihalakan secara khusus untuk mengukur kompetensi komunikasi pelajar terutamanya dalam aspek komunikasi lisan, interaksi dua hala, serta keupayaan menjadi pengantara dalam pelbagai konteks bahasa.<\/p>\n<p>Keperluan terhadap kompetensi lisan ini semakin mendesak memandangkan ramai profesional muda masa kini dikesan mampu berkomunikasi dengan berkesan secara dalam talian, namun bertukar menjadi pasif apabila terlibat dalam mesyuarat, pembentangan, mahupun perbincangan korporat yang menuntut respons masa nyata (<em>real-time<\/em>). Realiti ini disokong oleh data yang menunjukkan bahawa kemahiran interpersonal dan silang budaya menduduki tempat teratas dengan 81.5%, diikuti oleh kepimpinan serta kefasihan bahasa Inggeris yang masing-masing mencatatkan 66.7% (Economist Impact, 2023). Malah, sebanyak 40% majikan di Malaysia turut menyuarakan ketidakpuasan hati terhadap tahap komunikasi graduan (The Malaysia Reserve, 2026). Walaupun golongan ini secara umumnya fasih berbahasa Inggeris, mereka sering kali kekurangan kemahiran komunikasi kritikal di tempat kerja seperti keberanian untuk bersuara, kemahiran penulisan laporan profesional, dan penerapan kesantunan berbahasa.<\/p>\n<p><strong>Peralihan Corak Pengajaran Bahasa di Institusi Pengajian Tinggi <\/strong><\/p>\n<p>Menyedari cabaran ini, barisan pendidik Pengajaran Bahasa Inggeris (ELT) daripada seluruh universiti awam di Malaysia telah mula merangka hala tuju pendidikan bahasa Inggeris (Higher Education English Language Roadmap 2026-2036) yang komprehensif. Inisiatif ini merangkumi penetapan dasar AI, kaedah pentaksiran berbantukan AI, serta pelaksanaan pembelajaran berasaskan projek dan tugasan. Hala tuju ini bertindak sebagai &#8220;dokumen hidup&#8221; (<em>living document<\/em>) yang akan sentiasa dinamik dan dikemas kini mengikut acuan evolusi teknologi semasa. Melalui kecanggihan AI hari ini, seorang pelajar ijazah sarjana muda kini mampu memuat naik profil kompetensi khusus ke dalam AI bagi memastikan impak pembentangan serta gaya penulisan mereka mencapai piawaian setaraf peringkat Ph.D. Keupayaan teknologi ini merangkumi penguasaan gaya penulisan Bahasa Melayu standard Dewan Bahasa dan Pustaka (DBP), pematuhan format rujukan gaya APA, sehinggalah kebolehan meniru nuansa penulisan tokoh sasterawan terkemuka.<br \/>\n<center><br \/>\n<a href=\"https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture14.png\"><img decoding=\"async\" src=\"https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture14-300x181.png\" alt=\"\" width=\"300\" height=\"181\" class=\"alignnone size-medium wp-image-3071\" srcset=\"https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture14-300x181.png 300w, https:\/\/www.ukm.my\/wadahdigital\/wp-content\/uploads\/2026\/08\/Picture14.png 432w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/center><br \/>\n<center>Rajah 2: Taksanomi Bloom (Helpfulprofessor.com, 2026) <\/center><\/p>\n<p>Di Universiti Kebangsaan Malaysia (UKM), para guru bahasa Inggeris universiti mengaplikasikan pendekatan Taksonomi Bloom yang memberikan penekanan khusus kepada aras kognitif tinggi, iaitu menilai (evaluate) dan mencipta (create). Sebagai contoh, dalam kursus English for Academic Purposes, pelajar dilatih untuk mengenal pasti struktur hujah dalam esei serta menilai secara kritis sama ada maklumat yang disajikan bersifat fakta, pandangan, mahupun sekadar andaian. Sementara itu, bagi kursus Workplace English, tumpuan dialihkan kepada kemahiran industri seperti teknik penyampaian idea (pitching), pelaporan hasil kerja (reporting deliverables), serta pembelajaran berasaskan projek termasuklah merancang program universiti. Memandangkan landskap teknologi kini memacu perubahan dengan sangat pantas, para pendidik perlu menjalinkan kolaborasi erat dengan pihak industri bagi mengenal pasti jurang kemahiran yang wujud dan memastikan kurikulum yang ditawarkan kekal relevan dengan kehendak pasaran semasa.<\/p>\n<p><strong>Pedagogi Berdasarkan Nilai <\/strong><\/p>\n<p>Melangkah ke hadapan, integrasi erat antara pendidikan bahasa dan pedagogi berasaskan nilai (Values-Based Pedagogy) kini menjadi suatu keperluan. Transformasi ini mengubah orientasi pendidikan bahasa daripada sekadar penguasaan aspek teknikal seperti kosa kata dan struktur ayat, kepada proses pembentukan insaniah yang holistik bagi memupuk penaakulan moral, empati silang budaya, serta kesedaran sivik. AI perlu dilihat sebagai pemangkin utama yang berupaya menyinergikan kedua-dua elemen ini, sekali gus menjelmakan sistem pembelajaran yang dinamik bagi membentuk sahsiah dan pemikiran kritis pelajar, dan bukan sekadar berfungsi sebagai utiliti penyemak tatabahasa secara teknikal.<\/p>\n<p>Apabila ketiga-tiga elemen bahasa, nilai, dan kecerdasan buatan digabungkan, naratif pembelajaran secara tuntas bertukar arah daripada sekadar  berkomunikasi menggunakan AI kepada bagaimana berkomunikasi secara beretika, manusiawi, dan bertanggungjawab dalam dunia yang dipacu AI yang diperlukan di tempat kerja. Integrasi holistik ini menuntut kesiapsiagaan para pendidik untuk membimbing pelajar mengharungi pelbagai cabaran etika yang rencam. Cabaran utama melibatkan isu halusinasi dan ketidakbenaran, di mana AI kerap menjana fakta palsu secara meyakinkan; situasi yang memerlukan pelajar dilengkapi nilai &#8220;mencari kebenaran&#8221; (truth-seeking) agar tidak menerima maklumat secara melulu. Selain itu, kebergantungan yang keterlaluan terhadap kandungan generatif AI berisiko mengikis &#8220;suara manusia&#8221; (human agency), sekaligus menumpulkan keupayaan pelajar untuk meluahkan emosi tulen melalui bahasa hasil nukilan sendiri. Fenomena ini disusuli pula dengan risiko pencemaran budaya, memandangkan model AI arus perdana hari ini didominasi oleh pandangan Barat (Eurocentric), yang dikhuatiri mampu menghakis acuan nilai kebudayaan tempatan serta keaslian bahasa ibunda sekiranya tidak diimbangi melalui strategi pedagogi bahasa yang utuh.<\/p>\n<p><strong>Penutup<\/strong><\/p>\n<p>Secara tuntas, kehadiran AI dalam pendidikan bahasa ialah realiti yang menuntut tadbir urus yang strategik dan bijaksana. Langkah menstrukturkan semula kurikulum dengan mengarusperdanakan etika teknologi AI, pedagogi berasaskan nilai, serta kompetensi lisan bagi penuturan masa nyata di alam kerjaya adalah benteng utama dalam melahirkan graduan yang holistik. Melalui pendekatan ini, universiti awam Malaysia bukan sahaja melahirkan modal insan yang tangkas berbahasa, tetapi juga utuh sahsiah dan jati diri budayanya. Pendidik wajar bertindak sebagai pelopor transformasi ini demi memastikan keaslian ekosistem akademik dan lunas kemanusiaan sentiasa terpelihara mendepani arus automasi AI.<\/p>\n<p><strong>Rujukan  <\/strong><\/p>\n<p>[1] Poothuvallil, G. S., &#038; Manayath, D. (2026). From Prompt to Pedagogy: A Conceptual Framework for Structured AI Dialogue and Value-Based Learning. Information Systems for Intelligent Systems, 341\u2013350. <a href=\"https:\/\/doi.org\/10.1007\/978-3-032-12993-2_33\">https:\/\/doi.org\/10.1007\/978-3-032-12993-2_33<\/a><br \/>\n[2] Agarwal, S., &#038; Solanki, V. K. (2022). Creating awareness and implementing Blooms Taxonomy in teaching pedagogy\u2014with special reference to Higher Education. Proceedings of the International Conference on Research in Management &#038;  Technovation, 34, 75\u201379. <a href=\"https:\/\/doi.org\/10.15439\/2022m7014\">https:\/\/doi.org\/10.15439\/2022m7014<\/a><br \/>\n[3] Ritu Bandhari. (2023). Bridging the Skills Gap: Fuelling Careers and Economy in Malaysia. https:\/\/insights.economistenterprise.com\/trade-geopolitics\/bridging-skills-gap-fuelling-careers-and-economy-malaysia<br \/>\n[4] Azalea Azuar. (2026). The Missing Skills in Malaysia\u2019s Young Workforce. <a href=\"https:\/\/themalaysianreserve.com\/2026\/06\/23\/the-missing-skills-in-malaysias-young-workforce\/\">https:\/\/themalaysianreserve.com\/2026\/06\/23\/the-missing-skills-in-malaysias-young-workforce\/<\/a><br \/>\n[5] The Star. (2026). Alleged Fabricated Research Tarnishes Indonesian Academic Reputation. <a href=\"https:\/\/www.thestar.com.my\/aseanplus\/aseanplus-news\/2026\/05\/30\/alleged-fabricated-research-tarnishes-indonesian-academic-reputation\">https:\/\/www.thestar.com.my\/aseanplus\/aseanplus-news\/2026\/05\/30\/alleged-fabricated-research-tarnishes-indonesian-academic-reputation<\/a><\/p>\n   <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\r\n   <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Sora:wght@600;700;800&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">\r\n   <style>\r\n       #wd-widget{\r\n           --navy-deep:#0B1A2C;\r\n           --navy-surface:#132844;\r\n           --navy-surface-2:#1B3454;\r\n           --line-soft:rgba(255,255,255,0.09);\r\n           --blue:#3E86E0;\r\n           --blue-soft:#8FC2FF;\r\n           --gold:#FFC72C;\r\n           --red:#E14545;\r\n           --ink:#EAF1FA;\r\n           --ink-dim:#9FB3CC;\r\n           --ink-faint:#5E7793;\r\n           font-family:'Inter',-apple-system,'Segoe UI',sans-serif;\r\n           position:fixed;right:26px;bottom:26px;z-index:99999;\r\n           display:flex;flex-direction:column;align-items:flex-end;\r\n       }\r\n       #wd-widget *{box-sizing:border-box}\r\n       #wd-widget h1{font-family:'Sora',system-ui,sans-serif}\r\n       #wd-widget .wd-mono{font-family:'JetBrains Mono',monospace}\r\n\r\n       #wd-launcher{\r\n           position:relative;width:58px;height:58px;border-radius:50%;border:none;cursor:pointer;\r\n           background:linear-gradient(135deg,var(--blue),#5aa3f0);color:#fff;\r\n           display:flex;align-items:center;justify-content:center;font-size:24px;flex-shrink:0;\r\n           box-shadow:0 10px 30px -8px rgba(62,134,224,.6);\r\n           transition:transform .2s ease;\r\n       }\r\n       #wd-launcher:hover{transform:scale(1.06)}\r\n       #wd-launcher:active{transform:scale(.94)}\r\n       #wd-launcher .wd-pulse{\r\n           position:absolute;inset:-4px;border-radius:50%;border:2px solid var(--blue-soft);\r\n           opacity:0;animation:wd-pulse-anim 2.6s ease-out infinite;\r\n       }\r\n       @keyframes wd-pulse-anim{0%{opacity:.6;transform:scale(.9)}100%{opacity:0;transform:scale(1.35)}}\r\n       #wd-launcher svg{width:24px;height:24px;position:absolute;transition:opacity .15s,transform .15s}\r\n       #wd-launcher .wd-icon-close{opacity:0;transform:rotate(-45deg) scale(.6)}\r\n       #wd-widget.wd-open #wd-launcher .wd-icon-chat{opacity:0;transform:rotate(45deg) scale(.6)}\r\n       #wd-widget.wd-open #wd-launcher .wd-icon-close{opacity:1;transform:none}\r\n       #wd-badge{\r\n           position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;padding:0 5px;border-radius:999px;\r\n           background:var(--red);color:#fff;font-size:11px;font-weight:700;line-height:20px;text-align:center;\r\n           border:2px solid var(--navy-deep);\r\n       }\r\n\r\n       #wd-root{\r\n           position:absolute;bottom:calc(100% + 12px);right:0;\r\n           width:360px;max-width:calc(100vw - 32px);height:520px;max-height:calc(100vh - 140px);\r\n           background:var(--navy-surface);border:1px solid var(--line-soft);border-radius:20px;\r\n           display:flex;flex-direction:column;overflow:hidden;\r\n           box-shadow:0 24px 60px -16px rgba(0,0,0,.65);\r\n           opacity:0;transform:translateY(20px) scale(.98);pointer-events:none;\r\n           transform-origin:bottom right;\r\n           transition:opacity .25s cubic-bezier(.2,.9,.25,1.15),transform .25s cubic-bezier(.2,.9,.25,1.15);\r\n       }\r\n       #wd-widget.wd-open #wd-root{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}\r\n\r\n       #wd-header{\r\n           padding:16px 18px;border-bottom:1px solid var(--line-soft);\r\n           display:flex;align-items:center;justify-content:space-between;gap:10px;\r\n           background:var(--navy-surface-2);\r\n       }\r\n       #wd-header .who{display:flex;align-items:center;gap:10px;min-width:0}\r\n       #wd-header .mark{\r\n           width:32px;height:32px;border-radius:50%;flex-shrink:0;\r\n           background:linear-gradient(135deg,var(--blue),var(--gold));\r\n           display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:#14213d;\r\n       }\r\n       #wd-header h1{font-size:13.5px;margin:0;color:var(--ink);font-weight:700;\r\n           white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\r\n       #wd-header .status{display:flex;align-items:center;gap:5px;margin-top:2px}\r\n       #wd-header .status .dot{width:6px;height:6px;border-radius:50%;background:var(--gold);flex-shrink:0}\r\n       #wd-header .status span{font-size:10.5px;color:var(--ink-faint)}\r\n       #wd-close{background:none;border:none;color:var(--ink-dim);cursor:pointer;padding:4px;flex-shrink:0}\r\n       #wd-close:hover{color:var(--ink)}\r\n       #wd-close svg{width:14px;height:14px;display:block}\r\n       #wd-logout{margin-left:auto;font-size:11px;color:var(--ink-dim);text-decoration:none;white-space:nowrap;\r\n           padding:4px 8px;border:1px solid var(--line-soft);border-radius:8px;flex-shrink:0}\r\n       #wd-logout:hover{color:var(--gold);border-color:var(--gold)}\r\n\r\n       #wd-body{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}\r\n       #wd-body::-webkit-scrollbar{width:5px}\r\n       #wd-body::-webkit-scrollbar-thumb{background:var(--line-soft);border-radius:6px}\r\n\r\n       .wd-msg{max-width:85%;padding:10px 13px;border-radius:14px;font-size:13px;line-height:1.55;\r\n           animation:wd-in .22s ease both}\r\n       @keyframes wd-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}\r\n       .wd-msg.bot{background:var(--navy-surface-2);color:var(--ink);align-self:flex-start;border-bottom-left-radius:4px}\r\n       .wd-msg.user{background:linear-gradient(135deg,var(--blue),#3a72c4);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}\r\n       .wd-msg p{margin:0 0 7px}\r\n       .wd-msg p:last-child{margin-bottom:0}\r\n       .wd-msg strong{color:var(--gold)}\r\n       .wd-msg.user strong{color:#fff}\r\n\r\n       .wd-sources{margin-top:9px;display:flex;flex-direction:column;gap:6px}\r\n       .wd-source-tag{font-family:'JetBrains Mono',monospace;font-size:10px;text-transform:uppercase;\r\n           letter-spacing:.06em;color:var(--gold)}\r\n       .wd-source-link{display:block;font-size:12px;color:var(--blue-soft);text-decoration:none;\r\n           padding:6px 10px;background:var(--navy-surface);border:1px solid var(--line-soft);border-radius:8px;\r\n           transition:border-color .15s}\r\n       .wd-source-link:hover{border-color:var(--blue-soft)}\r\n\r\n       .wd-login-btn{display:inline-block;margin-top:10px;padding:8px 14px;border-radius:10px;\r\n           background:var(--gold);color:#241800!important;font-size:12.5px;font-weight:600;text-decoration:none}\r\n       .wd-login-btn:hover{opacity:.88}\r\n       .wd-login-alt{display:block;margin-top:8px;font-size:12px;color:var(--blue-soft)}\r\n\r\n       .wd-feedback-row{display:flex;gap:6px;margin-top:9px}\r\n       .wd-fb-btn{background:var(--navy-surface);border:1px solid var(--line-soft);border-radius:8px;\r\n           width:26px;height:26px;font-size:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;\r\n           transition:border-color .15s,background .15s}\r\n       .wd-fb-btn:hover:not(:disabled){border-color:var(--gold)}\r\n       .wd-fb-btn.wd-fb-active{border-color:var(--gold);background:rgba(255,199,44,.12)}\r\n       .wd-fb-btn:disabled{cursor:not-allowed;opacity:.55}\r\n\r\n       .wd-suggest-bubble{background:transparent!important;padding:0!important;max-width:100%!important}\r\n       .wd-chip-wrap{display:flex;flex-wrap:wrap;gap:6px}\r\n       .wd-chip{font-size:11px;background:var(--navy-surface-2);border:1px solid var(--line-soft);\r\n           color:var(--ink-dim);padding:6px 10px;border-radius:999px;cursor:pointer;font-family:inherit;\r\n           transition:border-color .15s,color .15s}\r\n       .wd-chip:hover:not(:disabled){border-color:var(--blue-soft);color:var(--blue-soft)}\r\n       .wd-chip:disabled{opacity:.5;cursor:not-allowed}\r\n\r\n       .wd-typing{display:flex;gap:4px;align-self:flex-start;padding:12px 14px;background:var(--navy-surface-2);border-radius:14px}\r\n       .wd-typing span{width:6px;height:6px;border-radius:50%;background:var(--ink-faint);animation:wd-bounce 1.2s infinite ease-in-out}\r\n       .wd-typing span:nth-child(2){animation-delay:.15s}\r\n       .wd-typing span:nth-child(3){animation-delay:.3s}\r\n       @keyframes wd-bounce{0%,60%,100%{transform:translateY(0);opacity:.5}30%{transform:translateY(-4px);opacity:1}}\r\n\r\n       #wd-inputbar{display:flex;gap:8px;padding:12px 12px 6px;border-top:1px solid var(--line-soft);background:var(--navy-surface-2)}\r\n       #wd-note{padding:0 14px 10px;background:var(--navy-surface-2);font-size:10px;color:var(--ink-faint);line-height:1.4}\r\n       #wd-input{flex:1;background:var(--navy-surface);border:1px solid var(--line-soft);color:var(--ink);\r\n           border-radius:10px;padding:10px 12px;font-size:13px;font-family:inherit;outline:none;transition:border-color .15s}\r\n       #wd-input::placeholder{color:var(--ink-faint)}\r\n       #wd-input:focus{border-color:var(--blue-soft)}\r\n       #wd-input:disabled{opacity:.5;cursor:not-allowed}\r\n       #wd-send{background:var(--gold);border:none;color:#241800;width:38px;border-radius:10px;cursor:pointer;\r\n           font-size:15px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .15s}\r\n       #wd-send:hover:not(:disabled){opacity:.88}\r\n       #wd-send:disabled{opacity:.5;cursor:not-allowed}\r\n\r\n       \/* ===== TEMA CERAH (selari dengan i-GEM): kad putih, kepala navy ===== *\/\r\n       #wd-widget{\r\n           --navy-deep:#0B1D6B;\r\n           --navy-surface:#FFFFFF;\r\n           --navy-surface-2:#EEF2F8;\r\n           --line-soft:#DDE3F0;\r\n           --blue:#0B1D6B;\r\n           --blue-soft:#1E56C4;\r\n           --gold:#0B1D6B;\r\n           --red:#E14545;\r\n           --ink:#0F1A3D;\r\n           --ink-dim:#5A6685;\r\n           --ink-faint:#8A93AD;\r\n       }\r\n       #wd-launcher{background:linear-gradient(135deg,#0B1D6B,#13298A);box-shadow:0 10px 30px -8px rgba(11,29,107,.55)}\r\n       #wd-root{background:#FFFFFF;border:1px solid #DDE3F0;box-shadow:0 24px 60px -16px rgba(11,29,107,.35)}\r\n       #wd-header{background:#0B1D6B;border-bottom:none}\r\n       #wd-header h1{color:#FFFFFF}\r\n       #wd-header .mark{background:#FFFFFF;color:#0B1D6B}\r\n       #wd-header .status .dot{background:#4ADE80}\r\n       #wd-header .status span{color:#C9D6F2}\r\n       #wd-close{color:#C9D6F2}\r\n       #wd-close:hover{color:#FFFFFF}\r\n       #wd-logout{color:#C9D6F2;border-color:rgba(255,255,255,.35)}\r\n       #wd-logout:hover{color:#FFFFFF;border-color:#FFFFFF}\r\n       .wd-msg.bot{background:#EEF2F8;color:#0F1A3D}\r\n       .wd-msg.user{background:#0B1D6B;color:#FFFFFF}\r\n       .wd-msg strong{color:#0B1D6B}\r\n       .wd-msg.user strong{color:#FFFFFF}\r\n       .wd-source-tag{color:#0B1D6B}\r\n       .wd-source-link{background:#FFFFFF;color:#1E56C4}\r\n       .wd-login-btn{background:#0B1D6B;color:#FFFFFF!important}\r\n       .wd-login-alt{color:#0B1D6B;text-decoration:none}\r\n       .wd-fb-btn.wd-fb-active{background:rgba(11,29,107,.08)}\r\n       .wd-chip-wrap{flex-direction:column;align-items:stretch}\r\n       .wd-chip{background:#FFFFFF;color:#0F1A3D;border-radius:8px;text-align:left;font-size:12px;line-height:1.4;padding:9px 12px}\r\n       .wd-chip:hover:not(:disabled){border-color:#0B1D6B;color:#0B1D6B}\r\n       .wd-typing{background:#EEF2F8}\r\n       #wd-inputbar,#wd-note{background:#FFFFFF}\r\n       #wd-input{background:#FFFFFF;color:#0F1A3D}\r\n       #wd-input:focus{border-color:#0B1D6B}\r\n       #wd-send{background:#0B1D6B;color:#FFFFFF}\r\n\r\n       @media (max-width:520px){\r\n           #wd-widget{right:16px;bottom:16px}\r\n           #wd-root{right:-6px;width:calc(100vw - 32px)}\r\n       }\r\n       @media (prefers-reduced-motion:reduce){.wd-msg,.wd-typing span,#wd-launcher .wd-pulse{animation:none!important}}\r\n   <\/style>\r\n   <div id=\"wd-widget\">\r\n       <div id=\"wd-root\" role=\"dialog\" aria-label=\"Pembantu Artikel Wadah Digital\" aria-hidden=\"true\">\r\n           <div id=\"wd-header\">\r\n               <div class=\"who\">\r\n                   <div class=\"mark\">WD<\/div>\r\n                   <div>\r\n                       <h1>Pembantu Artikel Wadah Digital<\/h1>\r\n                       <div class=\"status\"><span class=\"dot\"><\/span><span>Dalam talian &middot; sedia membantu<\/span><\/div>\r\n                   <\/div>\r\n               <\/div>\r\n               <a id=\"wd-logout\" href=\"#\" style=\"display:none\">Log keluar<\/a>\r\n               <button id=\"wd-close\" aria-label=\"Tutup chatbot\" type=\"button\">\r\n                   <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M6 6l12 12M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/><\/svg>\r\n               <\/button>\r\n           <\/div>\r\n           <div id=\"wd-body\"><\/div>\r\n           <div id=\"wd-inputbar\">\r\n               <input id=\"wd-input\" type=\"text\" placeholder=\"Contoh: Apa itu pengaturcaraan vibe?\" autocomplete=\"off\" \/>\r\n               <button id=\"wd-send\" aria-label=\"Hantar soalan\" type=\"button\">\u27a4<\/button>\r\n           <\/div>\r\n           <div id=\"wd-note\">Soalan dan jawapan anda direkodkan untuk penambahbaikan perkhidmatan.<\/div>\r\n       <\/div>\r\n       <button id=\"wd-launcher\" type=\"button\" aria-label=\"Buka chatbot Pembantu Artikel Wadah Digital\" aria-expanded=\"false\">\r\n           <span class=\"wd-pulse\"><\/span>\r\n           <svg class=\"wd-icon-chat\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z\" fill=\"currentColor\"\/><\/svg>\r\n           <svg class=\"wd-icon-close\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M6 6l12 12M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"2.3\" stroke-linecap=\"round\"\/><\/svg>\r\n           <span id=\"wd-badge\" style=\"display:none\">0<\/span>\r\n       <\/button>\r\n   <\/div>\r\n   <script>\r\n       (function() {\r\n           const endpoint = \"https:\\\/\\\/www.ukm.my\\\/wadahdigital\\\/wp-json\\\/wadah-chatbot\\\/v1\\\/ask\";\r\n           const suggestionsEndpoint = \"https:\\\/\\\/www.ukm.my\\\/wadahdigital\\\/wp-json\\\/wadah-chatbot\\\/v1\\\/suggestions\";\r\n           const feedbackEndpoint = \"https:\\\/\\\/www.ukm.my\\\/wadahdigital\\\/wp-json\\\/wadah-chatbot\\\/v1\\\/feedback\";\r\n           const ajaxUrl = \"https:\\\/\\\/www.ukm.my\\\/wadahdigital\\\/wp-admin\\\/admin-ajax.php\";\r\n           let loggedIn = false, userName = '', restNonce = '', idleMinutes = 0, idleTimer = null;\r\n\r\n           \/\/ Selepas log keluar automatik, halaman dimuat semula dengan ?wd_idle=1 \u2014 baca sekali, kemudian buang dari URL\r\n           let idleExpired = false;\r\n           try {\r\n               const u0 = new URL(location.href);\r\n               if (u0.searchParams.get('wd_idle') === '1') {\r\n                   idleExpired = true;\r\n                   u0.searchParams.delete('wd_idle');\r\n                   history.replaceState(null, '', u0.toString());\r\n               }\r\n           } catch (e) {}\r\n           \/\/ Kembali dari log masuk: ?wd_chat=1 \u2014 buka chatbot sendiri sekali, kemudian buang dari URL\r\n           let openAfterLogin = false;\r\n           try {\r\n               const u1 = new URL(location.href);\r\n               if (u1.searchParams.get('wd_chat') === '1') {\r\n                   openAfterLogin = true;\r\n                   u1.searchParams.delete('wd_chat');\r\n                   history.replaceState(null, '', u1.toString());\r\n               }\r\n           } catch (e) {}\r\n           \/\/ Tanda \"buka chatbot selepas log masuk\" disimpan dalam pelayar (bukan dalam alamat, kerana plugin lain\r\n           \/\/ boleh merosakkan simbol & dalam alamat). Sah selama 30 minit.\r\n           const PENDING_KEY = 'wdChatOpenAfterLogin';\r\n           function markPendingOpen() { try { localStorage.setItem(PENDING_KEY, String(Date.now())); } catch (e) {} }\r\n           function hasPendingOpen() {\r\n               try {\r\n                   const t = parseInt(localStorage.getItem(PENDING_KEY) || '0', 10);\r\n                   return t > 0 && (Date.now() - t) < 30 * 60 * 1000;\r\n               } catch (e) { return false; }\r\n           }\r\n           function clearPendingOpen() { try { localStorage.removeItem(PENDING_KEY); } catch (e) {} }\r\n           function idleRedirectUrl() {\r\n               try { const u = new URL(location.href); u.searchParams.set('wd_idle', '1'); return u.toString(); }\r\n               catch (e) { return location.href; }\r\n           }\r\n           let loginUrl = \"https:\\\/\\\/www.ukm.my\\\/wadahdigital\\\/masuk-i-gem\\\/?redirect_to=https%3A%2F%2Fwww.ukm.my%2Fwadahdigital%2Fwp-json%2Fwp%2Fv2%2Fposts%2F3068%3Fwd_chat%3D1\";\r\n           const requireLogin = true;\r\n           let registerUrl = \"https:\\\/\\\/www.ukm.my\\\/wadahdigital\\\/masuk-i-gem\\\/?tab=daftar&redirect_to=https%3A%2F%2Fwww.ukm.my%2Fwadahdigital%2Fwp-json%2Fwp%2Fv2%2Fposts%2F3068%3Fwd_chat%3D1\";\r\n\r\n           const widget = document.getElementById('wd-widget');\r\n           const root = widget.querySelector('#wd-root');\r\n           const launcher = widget.querySelector('#wd-launcher');\r\n           const closeBtn = widget.querySelector('#wd-close');\r\n           const badge = widget.querySelector('#wd-badge');\r\n           const body = root.querySelector('#wd-body');\r\n           const input = root.querySelector('#wd-input');\r\n           const sendBtn = root.querySelector('#wd-send');\r\n           const noteEl = root.querySelector('#wd-note');\r\n           const logoutEl = root.querySelector('#wd-logout');\r\n\r\n           \/\/ Header wajib untuk REST API: tanpa X-WP-Nonce, WordPress anggap pengguna belum log masuk\r\n           function apiHeaders(extra) {\r\n               \/\/ Nonce kosong dihantar sebagai header akan ditolak (403) walaupun untuk tetamu, jadi hantar hanya jika ada\r\n               const base = restNonce ? { 'X-WP-Nonce': restNonce } : {};\r\n               return Object.assign(base, extra || {});\r\n           }\r\n\r\n           \/\/ Tanya server: siapa pengguna sekarang + nonce segar. Tak terkesan oleh cache halaman.\r\n           async function refreshSession(forIdle) {\r\n               try {\r\n                   const res = await fetch(ajaxUrl, {\r\n                       method: 'POST',\r\n                       credentials: 'same-origin',\r\n                       headers: { 'Content-Type': 'application\/x-www-form-urlencoded' },\r\n                       body: 'action=wadah_chatbot_session&redirect=' + encodeURIComponent(forIdle ? idleRedirectUrl() : location.href)\r\n                   });\r\n                   const data = await res.json();\r\n                   loggedIn = !!data.loggedIn;\r\n                   userName = data.name || '';\r\n                   idleMinutes = parseInt(data.idleMinutes || 0, 10) || 0;\r\n                   restNonce = data.nonce || '';\r\n                   \/\/ URL log masuk\/daftar segar dari server (bukan yang dibakar dalam halaman cache)\r\n                   if (!loggedIn && data.loginUrl) {\r\n                       loginUrl = data.loginUrl;\r\n                       registerUrl = data.registerUrl || '';\r\n                       root.querySelectorAll('.wd-login-btn').forEach(a => { a.href = loginUrl; });\r\n                       root.querySelectorAll('.wd-login-alt').forEach(a => { if (registerUrl) a.href = registerUrl; });\r\n                   }\r\n                   \/\/ Pautan kekal tersembunyi: hanya digunakan oleh log keluar automatik (idle)\r\n                   if (loggedIn && data.logoutUrl) { logoutEl.href = data.logoutUrl; }\r\n               } catch (e) {\r\n                   loggedIn = false; userName = ''; restNonce = '';\r\n                   logoutEl.style.display = 'none';\r\n               }\r\n           }\r\n\r\n           let conversationHistory = [];\r\n           let lastSourceIds = [];\r\n           let isOpen = false;\r\n           let unreadCount = 0;\r\n           let chatStarted = false;\r\n\r\n           function updateBadge() {\r\n               if (unreadCount > 0) {\r\n                   badge.textContent = unreadCount > 9 ? '9+' : String(unreadCount);\r\n                   badge.style.display = 'flex';\r\n               } else {\r\n                   badge.style.display = 'none';\r\n               }\r\n           }\r\n           function openChat() {\r\n               isOpen = true;\r\n               widget.classList.add('wd-open');\r\n               root.setAttribute('aria-hidden', 'false');\r\n               launcher.setAttribute('aria-expanded', 'true');\r\n               unreadCount = 0;\r\n               updateBadge();\r\n               if (!chatStarted) { chatStarted = true; startConversation(); }\r\n               body.scrollTop = body.scrollHeight;\r\n           }\r\n           function closeChat() {\r\n               isOpen = false;\r\n               widget.classList.remove('wd-open');\r\n               root.setAttribute('aria-hidden', 'true');\r\n               launcher.setAttribute('aria-expanded', 'false');\r\n           }\r\n           launcher.addEventListener('click', () => { isOpen ? closeChat() : openChat(); });\r\n           closeBtn.addEventListener('click', closeChat);\r\n\r\n           function addUserMsg(text) {\r\n               const row = document.createElement('div');\r\n               row.className = 'wd-msg user';\r\n               row.textContent = text;\r\n               body.appendChild(row); body.scrollTop = body.scrollHeight;\r\n           }\r\n           function addTyping() {\r\n               const row = document.createElement('div');\r\n               row.className = 'wd-typing'; row.id = 'wd-typing-row';\r\n               row.innerHTML = '<span><\/span><span><\/span><span><\/span>';\r\n               body.appendChild(row); body.scrollTop = body.scrollHeight;\r\n           }\r\n           function removeTyping() { const t = document.getElementById('wd-typing-row'); if (t) t.remove(); }\r\n\r\n           \/\/ Escape HTML dahulu; hanya **bold** dibenarkan jadi <strong>\r\n           function renderInlineMarkdown(el, line) {\r\n               el.innerHTML = line\r\n                   .replace(\/&\/g, '&amp;')\r\n                   .replace(\/<\/g, '&lt;')\r\n                   .replace(\/>\/g, '&gt;')\r\n                   .replace(\/\\*\\*(.+?)\\*\\*\/g, '<strong>$1<\/strong>');\r\n           }\r\n           function addBotMsg(text, sources, feedbackCtx) {\r\n               const row = document.createElement('div'); row.className = 'wd-msg bot';\r\n               (text || '').split(\/\\n+\/).filter(Boolean).forEach(line => { const p = document.createElement('p'); renderInlineMarkdown(p, line); row.appendChild(p); });\r\n               if (sources && sources.length) {\r\n                   const wrap = document.createElement('div'); wrap.className = 'wd-sources';\r\n                   const tag = document.createElement('div'); tag.className = 'wd-source-tag'; tag.textContent = 'Sumber artikel';\r\n                   wrap.appendChild(tag);\r\n                   sources.forEach(s => { const a = document.createElement('a'); a.href = s.url; a.target = '_blank'; a.rel = 'noopener'; a.className = 'wd-source-link'; a.textContent = s.title; wrap.appendChild(a); });\r\n                   row.appendChild(wrap);\r\n               }\r\n               if (feedbackCtx) {\r\n                   const fbRow = document.createElement('div'); fbRow.className = 'wd-feedback-row';\r\n                   const up = document.createElement('button'); up.type = 'button'; up.className = 'wd-fb-btn'; up.textContent = '\ud83d\udc4d'; up.setAttribute('aria-label', 'Jawapan membantu');\r\n                   const down = document.createElement('button'); down.type = 'button'; down.className = 'wd-fb-btn'; down.textContent = '\ud83d\udc4e'; down.setAttribute('aria-label', 'Jawapan tidak membantu');\r\n                   const sendFeedback = (rating, btn) => {\r\n                       up.disabled = true; down.disabled = true;\r\n                       btn.classList.add('wd-fb-active');\r\n                       fetch(feedbackEndpoint, {\r\n                           method: 'POST',\r\n                           credentials: 'same-origin',\r\n                           headers: apiHeaders({ 'Content-Type': 'application\/json' }),\r\n                           body: JSON.stringify({ log_id: feedbackCtx.logId, rating })\r\n                       }).catch(() => {});\r\n                   };\r\n                   up.addEventListener('click', () => sendFeedback('up', up));\r\n                   down.addEventListener('click', () => sendFeedback('down', down));\r\n                   fbRow.appendChild(up); fbRow.appendChild(down);\r\n                   row.appendChild(fbRow);\r\n               }\r\n               body.appendChild(row); body.scrollTop = body.scrollHeight;\r\n               if (!isOpen) { unreadCount++; updateBadge(); }\r\n           }\r\n\r\n           \/\/ Mesej arahan log masuk (pelawat belum login \/ sesi tamat)\r\n           function addLoginPrompt(message) {\r\n               const row = document.createElement('div'); row.className = 'wd-msg bot';\r\n               const p = document.createElement('p'); p.textContent = message; row.appendChild(p);\r\n               const a = document.createElement('a'); a.href = loginUrl; a.className = 'wd-login-btn'; a.textContent = 'Log masuk';\r\n               a.addEventListener('click', markPendingOpen);\r\n               row.appendChild(a);\r\n               if (registerUrl) {\r\n                   const r = document.createElement('a'); r.href = registerUrl; r.className = 'wd-login-alt'; r.textContent = 'Belum ada akaun? Daftar di sini';\r\n                   r.addEventListener('click', markPendingOpen);\r\n                   row.appendChild(r);\r\n               }\r\n               body.appendChild(row); body.scrollTop = body.scrollHeight;\r\n               if (!isOpen) { unreadCount++; updateBadge(); }\r\n           }\r\n\r\n           function addSuggestionChips(items) {\r\n               if (!items || !items.length) return;\r\n               const row = document.createElement('div'); row.className = 'wd-msg bot wd-suggest-bubble';\r\n               const wrap = document.createElement('div'); wrap.className = 'wd-chip-wrap';\r\n               items.forEach(item => {\r\n                   const chip = document.createElement('button');\r\n                   chip.type = 'button'; chip.className = 'wd-chip'; chip.textContent = item.label;\r\n                   chip.addEventListener('click', () => {\r\n                       wrap.querySelectorAll('.wd-chip').forEach(c => c.disabled = true);\r\n                       handleAsk(item.question);\r\n                   });\r\n                   wrap.appendChild(chip);\r\n               });\r\n               row.appendChild(wrap);\r\n               body.appendChild(row); body.scrollTop = body.scrollHeight;\r\n           }\r\n           async function loadSuggestions() {\r\n               try {\r\n                   const res = await fetch(suggestionsEndpoint, { credentials: 'same-origin', headers: apiHeaders() });\r\n                   if (!res.ok) return;\r\n                   const data = await res.json();\r\n                   if (data && data.suggestions) addSuggestionChips(data.suggestions);\r\n               } catch (err) { \/* senyap \u2014 cadangan bukan fungsi kritikal *\/ }\r\n           }\r\n\r\n           async function handleAsk(question) {\r\n               if ((requireLogin && !loggedIn) || !question.trim()) return;\r\n\r\n               addUserMsg(question); input.value = ''; sendBtn.disabled = true; addTyping();\r\n               try {\r\n                   const send = () => fetch(endpoint, {\r\n                       method: 'POST',\r\n                       credentials: 'same-origin',\r\n                       headers: apiHeaders({ 'Content-Type': 'application\/json' }),\r\n                       body: JSON.stringify({\r\n                           question,\r\n                           history: conversationHistory.slice(-6),\r\n                           last_ids: lastSourceIds\r\n                       })\r\n                   });\r\n                   let res = await send();\r\n                   if (res.status === 401 || res.status === 403) {\r\n                       \/\/ Nonce lapuk? Segarkan senyap-senyap dan cuba sekali lagi sebelum menyerah.\r\n                       await refreshSession();\r\n                       if (loggedIn) res = await send();\r\n                   }\r\n                   const data = await res.json();\r\n                   removeTyping();\r\n                   if (res.status === 401 || res.status === 403) {\r\n                       \/\/ Sesi tamat atau nonce lapuk (cth halaman lama dibiar terbuka)\r\n                       addLoginPrompt('Sesi anda telah tamat. Sila log masuk semula untuk meneruskan.');\r\n                       input.disabled = true; sendBtn.disabled = true;\r\n                       return;\r\n                   }\r\n                   if (data.error) {\r\n                       addBotMsg('Ralat: ' + data.error, []);\r\n                   } else if (data.no_feedback) {\r\n                       addBotMsg(data.answer, data.sources);\r\n                   } else {\r\n                       addBotMsg(data.answer, data.sources, data.log_id ? { logId: data.log_id } : null);\r\n                       conversationHistory.push({ role: 'user', text: question });\r\n                       conversationHistory.push({ role: 'model', text: data.answer });\r\n                       if (data.sources && data.sources.length) {\r\n                           lastSourceIds = data.sources.map(s => s.id).filter(Boolean);\r\n                       }\r\n                   }\r\n               } catch (err) {\r\n                   removeTyping();\r\n                   addBotMsg('Ralat menghubungi pelayan. Sila cuba lagi.', []);\r\n               } finally {\r\n                   if (!input.disabled) sendBtn.disabled = false;\r\n               }\r\n           }\r\n           sendBtn.addEventListener('click', () => handleAsk(input.value));\r\n           input.addEventListener('keydown', e => { if (e.key === 'Enter') handleAsk(input.value); });\r\n\r\n           async function startConversation() {\r\n               input.disabled = true; sendBtn.disabled = true;\r\n               await refreshSession();\r\n               if (loggedIn || !requireLogin) {\r\n                   input.disabled = false; sendBtn.disabled = false;\r\n                   if (isOpen) setTimeout(() => input.focus(), 100);\r\n               }\r\n               if (!loggedIn && requireLogin) {\r\n                   input.disabled = true; sendBtn.disabled = true;\r\n                   input.placeholder = 'Log masuk untuk bertanya';\r\n                   noteEl.style.display = 'none';\r\n                   addLoginPrompt(idleExpired\r\n                       ? 'Anda telah dilog keluar secara automatik kerana tiada aktiviti. Sila log masuk semula untuk meneruskan.'\r\n                       : 'Salam! Chatbot ini hanya untuk pengguna berdaftar. Sila log masuk untuk mula bertanya tentang artikel Wadah Digital.');\r\n                   return;\r\n               }\r\n               if (!loggedIn && idleExpired) {\r\n                   addBotMsg('Anda telah dilog keluar secara automatik kerana tiada aktiviti.', []);\r\n               }\r\n               addBotMsg(loggedIn\r\n                   ? 'Salam, ' + userName + '! Tanya saya tentang mana-mana artikel di Wadah Digital.'\r\n                   : 'Salam! Tanya saya tentang mana-mana artikel di Wadah Digital.', []);\r\n               loadSuggestions();\r\n           }\r\n\r\n           \/\/ --- Log keluar automatik bila tiada aktiviti. Masa aktiviti terakhir dikongsi antara\r\n           \/\/ tab melalui localStorage, jadi aktiviti di satu tab menghalang tab lain daripada tamat. ---\r\n           const LAST_KEY = 'wd_last_activity';\r\n           function startIdleWatch() {\r\n               if (!loggedIn || !idleMinutes || idleTimer) return;\r\n               let lastTouch = 0;\r\n               const touch = () => { try { localStorage.setItem(LAST_KEY, String(Date.now())); } catch (e) {} };\r\n               const onActivity = () => {\r\n                   const now = Date.now();\r\n                   if (now - lastTouch > 5000) { lastTouch = now; touch(); }\r\n               };\r\n               ['mousemove', 'keydown', 'scroll', 'touchstart', 'click'].forEach(ev =>\r\n                   window.addEventListener(ev, onActivity, { passive: true }));\r\n               touch();\r\n               idleTimer = setInterval(async () => {\r\n                   let last = 0;\r\n                   try { last = parseInt(localStorage.getItem(LAST_KEY) || '0', 10); } catch (e) {} \r\n                   if (!last) last = Date.now();\r\n                   if (Date.now() - last > idleMinutes * 60000) {\r\n                       clearInterval(idleTimer);\r\n                       \/\/ Minta URL log keluar yang segar (nonce baharu) sebelum log keluar\r\n                       await refreshSession(true);\r\n                       if (loggedIn && logoutEl.href && logoutEl.href !== '#') location.href = logoutEl.href;\r\n                   }\r\n               }, 30000);\r\n           }\r\n\r\n           \/\/ Semak sesi sekali selepas halaman dimuat supaya pemasa idle berjalan walaupun widget tak dibuka\r\n           setTimeout(async () => {\r\n               await refreshSession();\r\n               startIdleWatch();\r\n               if (idleExpired && !loggedIn) { chatStarted = true; startConversation(); openChat(); }\r\n               else if ((openAfterLogin || hasPendingOpen()) && loggedIn) {\r\n                   clearPendingOpen();\r\n                   if (!isOpen) { launcher.click(); }\r\n               }\r\n           }, 1500);\r\n\r\n           unreadCount = 1;\r\n           updateBadge();\r\n       })();\r\n   <\/script>\r\n   \n","protected":false},"excerpt":{"rendered":"<p>Maryam Mohamed Amin Pusat Pengajian Citra Universiti (PPCU) * Penulis Koresponden : maryam.amin@ukm.edu.my Kata Kunci: Pendidikan Bahasa, Kecerdasan Buatan, Pedagogi Berasaskan Nilai, Pertuturan Masa Nyata,<a class=\"ut-readmore\" href=\"https:\/\/www.ukm.my\/wadahdigital\/merintis-masa-depan-peranan-ai-dalam-landskap-pengajaran-bahasa-di-ipt\/\"> &#8230;<\/a><\/p>\n","protected":false},"author":1,"featured_media":3070,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"sky_video_link_meta":"","footnotes":""},"categories":[59,53],"tags":[],"wd_jilid":[63],"class_list":["post-3068","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vol-4-julogos-2026","category-pendidikan-digital","wd_jilid-jilid-4"],"_links":{"self":[{"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/posts\/3068","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/comments?post=3068"}],"version-history":[{"count":4,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/posts\/3068\/revisions"}],"predecessor-version":[{"id":3091,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/posts\/3068\/revisions\/3091"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/media\/3070"}],"wp:attachment":[{"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/media?parent=3068"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/categories?post=3068"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/tags?post=3068"},{"taxonomy":"wd_jilid","embeddable":true,"href":"https:\/\/www.ukm.my\/wadahdigital\/wp-json\/wp\/v2\/wd_jilid?post=3068"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}