[{"data":1,"prerenderedAt":9491},["ShallowReactive",2],{"blog":3},[4,1032,1947,2639,3925,5945,6578,6854,7209,8553,8929,9316],{"id":5,"title":6,"body":7,"description":1016,"extension":1017,"meta":1018,"navigation":542,"ogImage":520,"path":1028,"seo":1029,"stem":1030,"__hash__":1031},"blog/blog/saya-mencoba-claude-design-hasilnya-mind-blowing.md","Saya Mencoba Claude Design: Hasilnya Mind-Blowing",{"type":8,"value":9,"toc":981},"minimark",[10,34,37,42,49,54,59,62,78,89,92,96,99,137,141,144,177,181,188,190,194,197,204,207,235,237,241,244,247,275,282,284,288,291,297,299,303,310,334,341,347,349,353,356,370,373,379,385,387,391,404,412,418,424,426,430,437,462,465,471,477,479,483,486,493,495,499,502,504,508,515,521,524,543,551,553,557,564,566,570,584,590,593,597,600,608,615,617,621,628,633,635,639,642,646,653,664,667,671,681,684,688,691,707,710,715,729,732,737,757,764,768,775,781,788,799,802,806,816,819,867,873,876,882,884,888,891,898,901,927,930,932,942,951,957,973],[11,12,13],"blockquote",{},[14,15,16,17,24,25,33],"p",{},"TL;DR — Anthropic baru merilis ",[18,19,23],"a",{"href":20,"rel":21},"https://www.anthropic.com/news/claude-design-anthropic-labs",[22],"nofollow","Claude Design"," di Anthropic Labs. Saya coba langsung: kasih Figma file Morpheme Design, tunggu ~5 menit, dan dapat design system lengkap (typography, color, components, UI kit). Dari sana saya prompt sederhana \"buatkan aplikasi e-commerce\" dan hasilnya benar-benar mind-blowing. Output-nya HTML + JSX yang bisa di-export ke Claude Code — hasil akhirnya bisa langsung dilihat di ",[26,27,28],"strong",{},[18,29,32],{"href":30,"rel":31},"https://morpheme-store.vercel.app/",[22],"morpheme-store.vercel.app",".",[35,36],"hr",{},[38,39,41],"h2",{"id":40},"apa-itu-claude-design","Apa itu Claude Design?",[14,43,44,45,33],{},"Sebelum masuk ke pengalaman aku, mari luruskan dulu apa itu Claude Design berdasarkan ",[18,46,48],{"href":20,"rel":47},[22],"post resmi Anthropic",[14,50,51,53],{},[26,52,23],{}," adalah produk baru di Anthropic Labs yang memungkinkan kolaborasi visual dengan Claude AI (powered by Claude Opus 4.7). Intinya: tool untuk bikin dan iterasi visual design lewat percakapan dengan AI.",[55,56,58],"h3",{"id":57},"untuk-siapa","Untuk siapa?",[14,60,61],{},"Ini bagian yang menarik. Di post resminya, Anthropic sebutkan audience-nya:",[63,64,65,72],"ul",{},[66,67,68,71],"li",{},[26,69,70],{},"Designer"," — yang mau eksplorasi lebih banyak arah desain",[66,73,74,77],{},[26,75,76],{},"Non-designer"," — \"for founders, product managers, and marketers with an idea but not a design background\"",[14,79,80,81,84,85,88],{},"Jadi target utamanya adalah ",[26,82,83],{},"desainer dan orang non-teknis"," yang punya ide tapi kesulitan meng-eksekusi secara visual. ",[26,86,87],{},"Developer tidak disebut eksplisit sebagai target audience"," — tapi ada fitur handoff ke Claude Code untuk implementasi.",[14,90,91],{},"Fun fact: aku developer, tapi tetap pakai tool ini dan ternyata tetap sangat membantu (lewat \"pintu belakang\" Claude Code). Lebih lanjut soal ini nanti di bagian catatan di akhir artikel 😄.",[55,93,95],{"id":94},"fitur-utama","Fitur utama",[14,97,98],{},"Menurut post resminya:",[63,100,101,107,113,119,125,131],{},[66,102,103,106],{},[26,104,105],{},"Design system integration"," — otomatis apply branding tim dari codebase & design files",[66,108,109,112],{},[26,110,111],{},"Multiple input formats"," — text prompt, upload gambar, import dokumen (DOCX, PPTX, XLSX), atau web capture",[66,114,115,118],{},[26,116,117],{},"Refinement tools"," — inline comments, direct text editing, custom adjustment controls",[66,120,121,124],{},[26,122,123],{},"Collaboration"," — sharing scope organisasi dengan permission view/edit",[66,126,127,130],{},[26,128,129],{},"Export flexibility"," — multi format: Canva, PDF, PPTX, HTML, internal URL",[66,132,133,136],{},[26,134,135],{},"Handoff ke Claude Code"," — untuk tahap implementasi",[55,138,140],{"id":139},"cara-kerjanya-versi-resmi","Cara kerjanya (versi resmi)",[14,142,143],{},"Anthropic mendeskripsikan workflow-nya seperti ini:",[145,146,147,153,159,165,171],"ol",{},[66,148,149,152],{},[26,150,151],{},"Setup brand"," saat onboarding",[66,154,155,158],{},[26,156,157],{},"Import atau create"," desain",[66,160,161,164],{},[26,162,163],{},"Iterasi"," lewat percakapan dan kontrol",[66,166,167,170],{},[26,168,169],{},"Kolaborasi"," dengan tim",[66,172,173,176],{},[26,174,175],{},"Export atau handoff"," ke Claude Code untuk development",[55,178,180],{"id":179},"akses","Akses",[14,182,183,184,187],{},"Claude Design tersedia untuk pengguna ",[26,185,186],{},"Claude Pro, Max, Team, dan Enterprise"," sebagai bagian dari plan yang sudah ada.",[35,189],{},[38,191,193],{"id":192},"kenapa-ini-menarik-dari-perspektif-developer","Kenapa ini menarik (dari perspektif developer)",[14,195,196],{},"Workflow design-to-code itu selalu penuh friksi. Desainer kerja di Figma, engineer translate ke kode, dan di tengah-tengah selalu ada informasi yang hilang — token yang tidak ter-sync, spacing yang di-approximate, komponen yang di-rebuild dari nol.",[14,198,199,200,203],{},"Claude Design mencoba memperpendek jarak itu secara drastis. Alurnya: ",[26,201,202],{},"Figma → Design System → Aplikasi jadi"," — dalam satu tool, konsisten end-to-end.",[14,205,206],{},"Walaupun target utamanya bukan developer, justru buat developer yang punya akses ke design system tim — tool ini bisa jadi shortcut yang powerful. Aku sudah coba end-to-end. Di bawah ini step-by-step-nya.",[11,208,209],{},[14,210,211,214,215,220,221,228,229,234],{},[26,212,213],{},"Catatan sebelum mulai:"," ",[18,216,219],{"href":217,"rel":218},"https://www.morpheme.design/",[22],"Morpheme Design"," adalah design system milik ",[26,222,223],{},[18,224,227],{"href":225,"rel":226},"https://gits.id",[22],"GITS.ID"," yang sudah kami bangun sebelumnya — lengkap dengan Figma file, fonts, komponen, dan guidelines. Kami juga sudah punya implementasi Vue-nya di ",[18,230,233],{"href":231,"rel":232},"https://ui.morpheme.design/",[22],"Morpheme UI",". Jadi saya punya starting point yang matang. Pengalamanmu mungkin berbeda, terutama kalau kamu belum punya design system sendiri. Di akhir artikel aku bahas lebih detail soal ini.",[35,236],{},[38,238,240],{"id":239},"_1-setup-morpheme-design","1. Setup Morpheme Design",[14,242,243],{},"Langkah pertama langsung tancap gas: setup Morpheme Design sebagai design system di Claude Design.",[14,245,246],{},"Yang perlu disiapkan:",[63,248,249,260,266,272],{},[66,250,251,254,255,259],{},[26,252,253],{},"Figma file"," (save as ",[256,257,258],"code",{},".fig"," file) — ini yang paling utama",[66,261,262,265],{},[26,263,264],{},"Fonts"," yang digunakan di desain — jangan sampai lupa",[66,267,268,271],{},[26,269,270],{},"Kode contoh"," dan link GitHub kalau ada",[66,273,274],{},"Sisanya bisa di-follow up lewat chat dengan Claude",[14,276,277],{},[278,279],"img",{"alt":280,"src":281},"Setup Morpheme Design di Claude Design","/images/claude-design/01-setup.jpeg",[35,283],{},[38,285,287],{"id":286},"_2-generate-design-system","2. Generate design system",[14,289,290],{},"Setelah semua bahan dikirim, tinggal generate.",[14,292,293],{},[278,294],{"alt":295,"src":296},"Proses creating Morpheme Design","/images/claude-design/02-creating.jpeg",[35,298],{},[38,300,302],{"id":301},"_3-tunggu-5-menit-worth-the-wait","3. Tunggu ~5 menit, worth the wait",[14,304,305,306,309],{},"Proses generate-nya agak lama — sekitar ",[26,307,308],{},"5 menit",". Tapi hasilnya worth banget. Claude sudah generate:",[63,311,312,318,323,328],{},[66,313,314,317],{},[26,315,316],{},"Typography"," (type scale lengkap)",[66,319,320],{},[26,321,322],{},"Color tokens",[66,324,325],{},[26,326,327],{},"Components library",[66,329,330,333],{},[26,331,332],{},"UI Kit"," sebagai contoh output",[14,335,336,337,340],{},"Semuanya ",[26,338,339],{},"based on Figma file"," yang aku kirim di awal. Jadi bukan template generik, tapi benar-benar turunan dari design source-ku.",[14,342,343],{},[278,344],{"alt":345,"src":346},"Hasil initial generate dari Claude Design","/images/claude-design/03-generated.jpeg",[35,348],{},[38,350,352],{"id":351},"_4-review-hasil-generate","4. Review hasil generate",[14,354,355],{},"Setelah design system selesai di-generate, kita bisa review hasilnya. Ada dua opsi sederhana:",[63,357,358,364],{},[66,359,360,363],{},[26,361,362],{},"Looks good"," → kalau hasilnya sudah sesuai",[66,365,366,369],{},[26,367,368],{},"Needs work"," → prompt lagi apa yang kurang, Claude akan adjust",[14,371,372],{},"Alur review-nya intuitif. Yang sudah oke di-approve, yang belum tinggal kasih feedback.",[14,374,375],{},[278,376],{"alt":377,"src":378},"Review result","/images/claude-design/04-review.jpeg",[14,380,381],{},[278,382],{"alt":383,"src":384},"Design system ready","/images/claude-design/05-ready.png",[35,386],{},[38,388,390],{"id":389},"_5-edit-manual-skillmd","5. Edit manual & SKILL.md",[14,392,393,394,399,400,403],{},"Yang menarik, file-file yang di-generate bisa kita edit manual juga. Dan yang bikin aku tersenyum: Claude juga generate file ",[26,395,396],{},[256,397,398],{},"SKILL.md"," bernama ",[256,401,402],{},"morpheme-design"," — mirip banget sama yang pernah aku buat sendiri sebelumnya.",[14,405,406,407,411],{},"Kalau kamu ikuti tulisanku sebelumnya tentang ",[18,408,410],{"href":409},"/blog/skill-claude-code-morpheme-design-system","membuat Skill Claude Code yang mengubah Design System jadi UI pixel-perfect",", konsepnya persis sama. Sekarang Claude Design yang bikinkan otomatis.",[14,413,414],{},[278,415],{"alt":416,"src":417},"Edit file SKILL manually","/images/claude-design/06-skill.jpeg",[14,419,420],{},[278,421],{"alt":422,"src":423},"Design files hasil generate","/images/claude-design/07-files.png",[35,425],{},[38,427,429],{"id":428},"_6-bikin-aplikasi-e-commerce","6. Bikin aplikasi E-Commerce",[14,431,432,433,436],{},"Dengan design system yang sudah siap, saatnya test case yang lebih ambisius: ",[26,434,435],{},"bikin design aplikasi E-Commerce lengkap",". Prompt-ku sederhana saja:",[11,438,439,442],{},[14,440,441],{},"create full e-commerce application with these features:",[63,443,444,447,450,453,456,459],{},[66,445,446],{},"Homepage",[66,448,449],{},"Product List",[66,451,452],{},"Product Detail",[66,454,455],{},"Cart",[66,457,458],{},"Checkout",[66,460,461],{},"Payment",[14,463,464],{},"Tidak perlu detail panjang. Design system sudah jadi guardrail — Claude tinggal apply.",[14,466,467],{},[278,468],{"alt":469,"src":470},"Testing create E-Commerce Design App","/images/claude-design/08-ecommerce-test.jpeg",[14,472,473],{},[278,474],{"alt":475,"src":476},"Prompt E-Commerce","/images/claude-design/09-prompt.png",[35,478],{},[38,480,482],{"id":481},"_7-claude-akan-banyak-bertanya","7. Claude akan banyak bertanya",[14,484,485],{},"Kalau context masih kurang, Claude akan banyak bertanya detail lewat UI (di canvas-nya). Sayang waktu itu lupa aku screenshot bagian ini.",[14,487,488,489,492],{},"Tipsnya: kalau bingung jawab apa, pilih aja ",[26,490,491],{},"\"Decide for me\"",". Biar Claude yang ambil keputusan berdasarkan best practice. Ini salah satu UX yang menurutku paling pas — tidak dipaksa memutuskan semua detail di depan.",[35,494],{},[38,496,498],{"id":497},"_8-tunggu-claude-bekerja","8. Tunggu Claude bekerja",[14,500,501],{},"Proses generate aplikasi juga butuh waktu. Santai aja, biarkan Claude mengerjakan.",[35,503],{},[38,505,507],{"id":506},"_9-boom-hasilnya-mind-blowing","9. BOOM! Hasilnya mind-blowing",[14,509,510,511,514],{},"Dan... hasilnya ",[26,512,513],{},"sangat mind-blowing",". Semua halaman ter-generate dengan konsisten mengikuti design system yang sudah di-setup sebelumnya. Warna, tipografi, spacing, komponen — semua sesuai token.",[14,516,517],{},[278,518],{"alt":519,"src":520},"Hasil akhir aplikasi E-Commerce","/images/claude-design/10-result.png",[14,522,523],{},"Buat yang mau lihat hasilnya dalam bentuk video (Morpheme Store output):",[525,526,530,531],"div",{"className":527},[528,529],"aspect-video","my-6","\n  ",[532,533],"iframe",{"src":534,"title":535,"className":536,"frameBorder":540,"allow":541,"allowFullScreen":542},"https://www.youtube.com/embed/PQkLS5bnRyk","Claude Design - Morpheme Store Output",[537,538,539],"w-full","h-full","rounded-lg","0","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",true,[14,544,545,546],{},"Atau ",[18,547,550],{"href":548,"rel":549},"https://youtu.be/PQkLS5bnRyk",[22],"tonton langsung di YouTube →",[35,552],{},[38,554,556],{"id":555},"_10-masih-bisa-di-tweak-lagi","10. Masih bisa di-tweak lagi",[14,558,559,560,563],{},"Yang perlu diingat: hasil generate ",[26,561,562],{},"bukan final",". Kita masih bisa tweak lagi — ganti warna, ubah layout, tambah komponen, atau adjust copy sesuai kebutuhan. Claude Design posisinya sebagai titik awal yang sangat kuat, bukan black box yang final.",[35,565],{},[38,567,569],{"id":568},"_11-export-ke-berbagai-target","11. Export ke berbagai target",[14,571,572,573,576,577,580,581,33],{},"Output akhirnya berupa ",[26,574,575],{},"HTML + JSX"," (kemungkinan untuk preview di canvas). Tapi yang bikin tool ini benar-benar useful: hasilnya bisa di-",[26,578,579],{},"export ke banyak target",", termasuk langsung ke ",[26,582,583],{},"Claude Code",[14,585,586],{},[278,587],{"alt":588,"src":589},"Claude Design export options","/images/claude-design/11-export.png",[14,591,592],{},"Dari sini alurnya jadi mulus — buka Claude Code, lanjutkan integrasi ke proyek nyata (stack kamu sendiri, API, routing, state management) tanpa harus re-build UI-nya dari nol.",[55,594,596],{"id":595},"live-demo-morpheme-store","Live demo: Morpheme Store",[14,598,599],{},"Sebagai bukti end-to-end flow-nya jalan, aku export hasil Claude Design ke Claude Code dan langsung publish ke Vercel:",[14,601,602,603],{},"🔗 ",[26,604,605],{},[18,606,32],{"href":30,"rel":607},[22],[14,609,610,611,614],{},"Jadi alur lengkap yang aku lalui: ",[26,612,613],{},"Figma → Claude Design → Design System → Aplikasi E-Commerce → Claude Code → Vercel",". Semua halaman (Homepage, Product List, Product Detail, Cart, Checkout, Payment) bisa langsung diakses live.",[35,616],{},[38,618,620],{"id":619},"catatan-kecil-bad-gateway","Catatan kecil: Bad Gateway",[14,622,623,624,627],{},"Di tengah proses ada satu kendala — sempat muncul ",[26,625,626],{},"Bad Gateway",". Wajar, tool ini masih di Anthropic Labs (experimental). Tinggal refresh dan lanjut.",[14,629,630],{},[278,631],{"alt":626,"src":632},"/images/claude-design/99-bad-gateway.png",[35,634],{},[38,636,638],{"id":637},"kenapa-ini-game-changer","Kenapa ini game-changer",[14,640,641],{},"Setelah aku coba end-to-end, ada dua hal yang menurutku bikin Claude Design beda dari sekadar \"AI yang generate UI\":",[55,643,645],{"id":644},"_1-setup-morpheme-design-cukup-1x-reusable-berkali-kali","1. Setup Morpheme Design cukup 1x, reusable berkali-kali",[14,647,648,649,652],{},"Investasi waktu 5 menit di awal untuk generate design system itu ",[26,650,651],{},"one-time cost",". Setelah itu, design system-nya bisa dipakai ulang untuk project apapun:",[63,654,655,658,661],{},[66,656,657],{},"Hari ini bikin aplikasi e-commerce",[66,659,660],{},"Besok bikin dashboard admin",[66,662,663],{},"Lusa bikin landing page",[14,665,666],{},"Semuanya pakai design system yang sama. Tidak perlu setup ulang. Tidak perlu upload Figma lagi. Cukup prompt, dan Claude langsung apply token yang benar.",[55,668,670],{"id":669},"_2-design-output-app-selalu-konsisten","2. Design / output app selalu konsisten",[14,672,673,674,676,677,680],{},"Ini yang menurutku paling valuable. Karena Claude baca source desain dan bikin aturannya sendiri (lewat ",[256,675,398],{}," yang ter-generate), setiap halaman yang di-generate ",[26,678,679],{},"selalu konsisten"," — warna, tipografi, spacing, komponen, semua sesuai token.",[14,682,683],{},"Tidak ada lagi drama \"kok biru di halaman ini beda sama halaman itu?\" atau \"spacing-nya kenapa tiba-tiba 14px padahal design system-nya basis 4px?\". Konsistensi yang biasanya harus dijaga manual lewat code review, sekarang ter-enforce otomatis.",[38,685,687],{"id":686},"catatan-penting-pengalaman-bisa-berbeda-antar-user","Catatan penting: pengalaman bisa berbeda antar user",[14,689,690],{},"Sebelum kamu langsung coba dan expect hasil yang sama, ada hal penting yang perlu aku sampaikan:",[14,692,693,696,697,702,703,706],{},[26,694,695],{},"Morpheme Design bukan design system dadakan."," Ini adalah design system milik ",[26,698,699],{},[18,700,227],{"href":225,"rel":701},[22]," (tempat aku bekerja) yang ",[26,704,705],{},"sudah kami bangun sebelumnya"," — dengan Figma file yang rapi, token yang terstruktur, komponen yang lengkap, fonts yang jelas, dan guidelines yang sudah matang.",[14,708,709],{},"Jadi saat aku upload ke Claude Design, input-nya sudah sangat berkualitas. Hasil yang aku dapat (5 menit → design system lengkap → aplikasi konsisten) sangat dipengaruhi oleh kualitas source-nya.",[14,711,712],{},[26,713,714],{},"Pengalamanmu bisa berbeda kalau:",[63,716,717,720,723,726],{},[66,718,719],{},"❌ Kamu belum punya design system sendiri",[66,721,722],{},"❌ Figma file-mu masih berantakan (tidak pakai auto layout, token warna acak, komponen tidak ter-organize)",[66,724,725],{},"❌ Tidak ada dokumentasi jelas soal spacing, tipografi, atau komponen",[66,727,728],{},"❌ Font yang dipakai tidak di-attach",[14,730,731],{},"Dalam kasus itu, Claude Design tetap bisa membantu — tapi dia harus \"menebak\" banyak hal, dan hasilnya akan mencerminkan apa yang kamu kasih. Garbage in, garbage out.",[14,733,734],{},[26,735,736],{},"Rekomendasi aku:",[145,738,739,745,751],{},[66,740,741,744],{},[26,742,743],{},"Kalau kamu punya design system:"," Rapikan dulu sebelum upload — token, komponen, auto layout, fonts. Investasi ini worth it.",[66,746,747,750],{},[26,748,749],{},"Kalau kamu belum punya design system:"," Gunakan Claude Design sebagai starting point — generate design system dari Figma sederhana, lalu iterasi via \"Needs work\" sampai sesuai keinginan. Ini cara yang lebih organik untuk membangun design system dari nol.",[66,752,753,756],{},[26,754,755],{},"Kalau kamu designer/developer solo:"," Coba dengan desain yang kamu sudah punya — walaupun belum sempurna, hasilnya bisa jadi foundation yang bagus untuk di-tweak.",[14,758,759,760,763],{},"Intinya: ",[26,761,762],{},"Claude Design adalah amplifier",". Dia memperkuat apa yang kamu kasih — bagus maupun jelek.",[38,765,767],{"id":766},"plot-twist-ternyata-ini-bukan-untuk-developer","Plot twist: ternyata ini bukan untuk developer 😅",[14,769,770,771,774],{},"Setelah selesai coba semuanya, aku baru baca ulang ",[18,772,48],{"href":20,"rel":773},[22]," — dan sadar satu hal:",[14,776,777,780],{},[26,778,779],{},"Claude Design itu dibuat untuk designer dan orang non-teknis"," (founders, PM, marketer). Developer tidak disebutkan sebagai target audience utama.",[14,782,783,784,787],{},"Aku — sebagai developer — masuk lewat \"pintu belakang\" via fitur handoff ke Claude Code. Dan ternyata... ",[26,785,786],{},"tetap powerful",". Bahkan mungkin ini jadi salah satu use case yang paling efisien buat developer:",[63,789,790,793,796],{},[66,791,792],{},"Punya akses ke design system tim? ✅",[66,794,795],{},"Mau eksperimen bikin UI baru tanpa harus nunggu mockup dari designer? ✅",[66,797,798],{},"Mau dapat starting point HTML/JSX yang konsisten? ✅",[14,800,801],{},"Jadi kalau kamu developer dan mau coba Claude Design, jangan minder — walaupun bukan target audience utama, tool ini tetap bisa jadi senjata yang powerful. Tinggal gimana caramu memanfaatkannya.",[55,803,805],{"id":804},"bonus-insight-untuk-developer-convert-ke-stack-favoritmu","Bonus insight untuk developer: convert ke stack favoritmu",[14,807,808,809,812,813,33],{},"Karena hasil export Claude Design ke Claude Code itu berupa ",[26,810,811],{},"HTML + CSS + JSX",", ini artinya kita punya ",[26,814,815],{},"material mentah yang bisa di-transform ke stack apapun",[14,817,818],{},"Contoh prompt lanjutan di Claude Code yang bisa kamu coba:",[63,820,821,828,835,846,853,860],{},[66,822,823,824,827],{},"\"Convert semua halaman ini ke ",[26,825,826],{},"Nuxt 3"," dengan composables, auto-imports, dan file-based routing\"",[66,829,830,831,834],{},"\"Port ke ",[26,832,833],{},"Next.js 15"," (App Router) dengan server components dan server actions\"",[66,836,837,838,841,842,845],{},"\"Convert ke ",[26,839,840],{},"Vue 3"," murni dengan ",[256,843,844],{},"\u003Cscript setup>"," dan Pinia untuk state management\"",[66,847,848,849,852],{},"\"Rewrite sebagai ",[26,850,851],{},"Laravel Blade"," views + Alpine.js untuk interaktivitas\"",[66,854,855,856,859],{},"\"Buat versi ",[26,857,858],{},"SvelteKit"," dengan stores\"",[66,861,862,863,866],{},"\"Integrate ke ",[26,864,865],{},"Inertia.js"," project yang sudah ada\"",[14,868,869,870,33],{},"Alurnya jadi: ",[26,871,872],{},"Figma → Claude Design → HTML/JSX → Claude Code → Stack favoritmu",[14,874,875],{},"Jadi kalau kamu bekerja di tim dengan stack yang sudah ditentukan (misalnya tim kami di GITS.ID banyak pakai Nuxt), kamu tetap bisa pakai Claude Design sebagai starting point, lalu minta Claude Code convert ke stack yang kamu pakai. Design system tetap konsisten, tinggal ganti \"bungkus framework\"-nya.",[14,877,878,879,33],{},"Ini yang menurutku bikin Claude Design relevan banget buat developer — bukan karena tool-nya di-design untuk kita, tapi karena ",[26,880,881],{},"output-nya cukup generic (HTML/JSX) untuk di-adapt ke stack apapun",[35,883],{},[38,885,887],{"id":886},"kesimpulan","Kesimpulan",[14,889,890],{},"Claude Design ini benar-benar mengubah cara pandang tentang workflow design-to-code. Dari satu Figma file bisa jadi design system, lalu dari design system bisa jadi aplikasi fungsional — semuanya dalam satu flow yang konsisten.",[14,892,893,894,897],{},"Yang menurutku paling powerful bukan fitur \"generate halaman-nya\" itu sendiri (karena itu sudah banyak tool yang bisa). Yang powerful adalah ",[26,895,896],{},"konsistensi antara design system dan hasil generate-nya",". Karena Claude membaca source desainmu dan membuat skill/rule sendiri, dia tidak halu — setiap halaman yang di-generate benar-benar memakai token yang benar.",[14,899,900],{},"Beberapa takeaway:",[145,902,903,909,915,921],{},[66,904,905,908],{},[26,906,907],{},"Siapkan Figma file yang rapi"," — garbage in, garbage out tetap berlaku. Design system yang di-generate akan serapi source-nya.",[66,910,911,914],{},[26,912,913],{},"Fonts itu krusial"," — jangan lupa attach fonts yang dipakai, biar tipografi tidak fallback ke default.",[66,916,917,920],{},[26,918,919],{},"Manfaatkan SKILL.md yang ter-generate"," — ini sama berharganya dengan design system itu sendiri. Kamu bisa bawa ke Claude Code dan konsistensi terus berlanjut.",[66,922,923,926],{},[26,924,925],{},"\"Decide for me\" is your friend"," — tidak perlu memutuskan semua detail di depan.",[14,928,929],{},"Kalau kamu punya design system internal dan mau coba AI tooling yang benar-benar respect terhadap design system itu, Claude Design layak banget dicoba.",[35,931],{},[14,933,934,937,938],{},[26,935,936],{},"Referensi",": ",[18,939,941],{"href":20,"rel":940},[22],"Introducing Claude Design — Anthropic Labs",[14,943,944,937,947,950],{},[26,945,946],{},"Live Demo",[18,948,32],{"href":30,"rel":949},[22]," — hasil Claude Design yang di-export ke Claude Code dan di-deploy ke Vercel",[14,952,953,956],{},[26,954,955],{},"Morpheme Design System"," (GITS.ID):",[63,958,959,966],{},[66,960,961,962],{},"🎨 Design system: ",[18,963,965],{"href":217,"rel":964},[22],"morpheme.design",[66,967,968,969],{},"🧩 Vue implementation: ",[18,970,972],{"href":231,"rel":971},[22],"ui.morpheme.design",[14,974,975,937,978],{},[26,976,977],{},"Related read",[18,979,980],{"href":409},"Saya Membuat Skill Claude Code yang Mengubah Design System Jadi UI Pixel-Perfect",{"title":982,"searchDepth":983,"depth":983,"links":984},"",2,[985,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1006,1007,1011,1012,1015],{"id":40,"depth":983,"text":41,"children":986},[987,989,990,991],{"id":57,"depth":988,"text":58},3,{"id":94,"depth":988,"text":95},{"id":139,"depth":988,"text":140},{"id":179,"depth":988,"text":180},{"id":192,"depth":983,"text":193},{"id":239,"depth":983,"text":240},{"id":286,"depth":983,"text":287},{"id":301,"depth":983,"text":302},{"id":351,"depth":983,"text":352},{"id":389,"depth":983,"text":390},{"id":428,"depth":983,"text":429},{"id":481,"depth":983,"text":482},{"id":497,"depth":983,"text":498},{"id":506,"depth":983,"text":507},{"id":555,"depth":983,"text":556},{"id":568,"depth":983,"text":569,"children":1004},[1005],{"id":595,"depth":988,"text":596},{"id":619,"depth":983,"text":620},{"id":637,"depth":983,"text":638,"children":1008},[1009,1010],{"id":644,"depth":988,"text":645},{"id":669,"depth":988,"text":670},{"id":686,"depth":983,"text":687},{"id":766,"depth":983,"text":767,"children":1013},[1014],{"id":804,"depth":988,"text":805},{"id":886,"depth":983,"text":887},"Anthropic baru merilis Claude Design di Anthropic Labs. Saya coba pakai design system Morpheme Design milik GITS.ID — dari Figma, generate design system, build aplikasi e-commerce, sampai deploy ke Vercel. Hasilnya mind-blowing.","md",{"date":1019,"keywords":1020,"tags":1021,"slug":1026,"status":1027},"2026-04-18T08:00:00+07:00","Claude Design, Anthropic, Anthropic Labs, Figma, Design System, Morpheme Design, Claude Code, AI, Frontend, Design-to-Code",[23,1022,1023,1024,1025],"Anthropic Labs","Design System","Figma","AI","saya-mencoba-claude-design-hasilnya-mind-blowing","published","/blog/saya-mencoba-claude-design-hasilnya-mind-blowing",{"title":6,"description":1016},"blog/saya-mencoba-claude-design-hasilnya-mind-blowing","hwSS_Jb5yiP51QDxZlJvSVNvczPpGLNwFwM1ckNbMaU",{"id":1033,"title":980,"body":1034,"description":1936,"extension":1017,"meta":1937,"navigation":542,"ogImage":1943,"path":409,"seo":1944,"stem":1945,"__hash__":1946},"blog/blog/skill-claude-code-morpheme-design-system.md",{"type":8,"value":1035,"toc":1908},[1036,1052,1054,1058,1061,1064,1070,1074,1083,1089,1115,1122,1126,1130,1139,1145,1211,1214,1218,1221,1227,1233,1239,1245,1251,1255,1258,1275,1279,1286,1351,1354,1358,1364,1368,1371,1419,1423,1426,1546,1549,1571,1578,1586,1590,1594,1624,1628,1660,1664,1706,1710,1716,1726,1730,1734,1739,1743,1773,1777,1783,1787,1794,1807,1811,1818,1824,1827,1831,1834,1871,1874,1878,1881,1890,1898,1900,1905],[11,1037,1038],{},[14,1039,1040,1041,1044,1045],{},"TL;DR — Saya mengemas design system Morpheme UI sebagai skill Claude Code. Sekarang setiap kali saya minta Claude membangun halaman, dia otomatis mengikuti token, spacing, tipografi, dan aturan aksesibilitas yang benar. Tidak perlu lagi copy-paste spec desain. Tidak ada lagi \"biru itu kok beda ya.\" Cukup ",[256,1042,1043],{},"/morpheme-design buat halaman pricing"," dan langsung jadi. ",[26,1046,1047],{},[18,1048,1051],{"href":1049,"rel":1050},"https://morpheme-design-md.vercel.app/",[22],"Lihat 10 contoh UI live →",[35,1053],{},[38,1055,1057],{"id":1056},"masalahnya-design-system-yang-tidak-ada-yang-ikuti","Masalahnya: design system yang tidak ada yang ikuti",[14,1059,1060],{},"Kalau kamu pernah kerja di tim produk, pasti paham situasi ini. Tim desain punya file Figma yang rapi — lengkap dengan token, komponen, dan guidelines. Tim engineering berusaha mengikutinya. Tapi di suatu titik antara \"di Figma 8px\" dan \"hardcode 10px aja deh, mirip-mirip\" — konsistensi mati.",[14,1062,1063],{},"Design system hanya berguna kalau benar-benar diterapkan. Dan menerapkannya secara manual itu melelahkan. Code review menangkap sebagian pelanggaran, linting menangkap sebagian lagi, tapi beban kognitif untuk mengingat shadow token yang mana yang harus dipakai atau seperti apa spec focus ring — itulah yang memperlambat.",[14,1065,1066,1067],{},"Jadi saya bertanya: ",[26,1068,1069],{},"bagaimana kalau AI-nya sudah tahu design system-nya?",[38,1071,1073],{"id":1072},"solusinya-skill-di-claude-code","Solusinya: skill di Claude Code",[14,1075,1076,1077,1082],{},"Claude Code punya fitur bernama ",[18,1078,1081],{"href":1079,"rel":1080},"https://docs.anthropic.com/en/docs/claude-code/skills",[22],"skills"," — file instruksi yang bisa dipakai ulang dan diinjeksi ke konteks saat dipicu. Anggap saja sebagai prompt khusus yang aktif berdasarkan apa yang sedang kamu kerjakan.",[14,1084,1085,1086,1088],{},"Saya membuat skill bernama ",[256,1087,402],{}," yang berisi semua aturan dari design system kami:",[63,1090,1091,1094,1097,1100,1103,1106,1109,1112],{},[66,1092,1093],{},"Token warna (arsitektur 3 lapisan: foundation → brand → semantic)",[66,1095,1096],{},"Spec tipografi (Poppins, type scale, hierarki weight)",[66,1098,1099],{},"Sistem spacing (basis 4px)",[66,1101,1102],{},"Spec komponen (button, input, card, badge, alert, modal, navigasi)",[66,1104,1105],{},"Skala shadow dan elevasi",[66,1107,1108],{},"Token motion dan animasi",[66,1110,1111],{},"Persyaratan aksesibilitas (rasio kontras, focus ring, ARIA)",[66,1113,1114],{},"Referensi CSS custom properties",[14,1116,1117,1118,1121],{},"Saat dipicu, skill ini memerintahkan Claude untuk membaca spec ",[256,1119,1120],{},"DESIGN.md"," secara lengkap dan menerapkan setiap aturan saat men-generate kode.",[38,1123,1125],{"id":1124},"cara-kerjanya","Cara kerjanya",[55,1127,1129],{"id":1128},"struktur-file","Struktur file",[1131,1132,1137],"pre",{"className":1133,"code":1135,"language":1136},[1134],"language-text","project-kamu/\n├── DESIGN.md                              # Spec design system lengkap\n├── .claude/\n│   └── skills/\n│       └── morpheme-design/\n│           └── SKILL.md                   # Definisi skill\n","text",[256,1138,1135],{"__ignoreMap":982},[14,1140,1141,1142,1144],{},"File skill (",[256,1143,398],{},") punya header YAML frontmatter yang memberi tahu Claude Code kapan harus aktif:",[1131,1146,1150],{"className":1147,"code":1148,"language":1149,"meta":982,"style":982},"language-yaml shiki shiki-themes github-light github-dark","---\nname: morpheme-design\ndescription: >\n  Enforce the Morpheme UI Design System when building web pages, apps, or UI components.\n  Ensures all design tokens, colors, typography, spacing, components, and accessibility\n  guidelines follow the Morpheme specification from DESIGN.md.\n  Use when user asks to build, create, or modify a web page, app, landing page,\n  dashboard, form, UI component, or any frontend interface. Also use when user asks\n  to style, theme, or design any web element.\n---\n","yaml",[256,1151,1152,1160,1165,1170,1176,1182,1188,1194,1200,1206],{"__ignoreMap":982},[1153,1154,1157],"span",{"class":1155,"line":1156},"line",1,[1153,1158,1159],{},"---\n",[1153,1161,1162],{"class":1155,"line":983},[1153,1163,1164],{},"name: morpheme-design\n",[1153,1166,1167],{"class":1155,"line":988},[1153,1168,1169],{},"description: >\n",[1153,1171,1173],{"class":1155,"line":1172},4,[1153,1174,1175],{},"  Enforce the Morpheme UI Design System when building web pages, apps, or UI components.\n",[1153,1177,1179],{"class":1155,"line":1178},5,[1153,1180,1181],{},"  Ensures all design tokens, colors, typography, spacing, components, and accessibility\n",[1153,1183,1185],{"class":1155,"line":1184},6,[1153,1186,1187],{},"  guidelines follow the Morpheme specification from DESIGN.md.\n",[1153,1189,1191],{"class":1155,"line":1190},7,[1153,1192,1193],{},"  Use when user asks to build, create, or modify a web page, app, landing page,\n",[1153,1195,1197],{"class":1155,"line":1196},8,[1153,1198,1199],{},"  dashboard, form, UI component, or any frontend interface. Also use when user asks\n",[1153,1201,1203],{"class":1155,"line":1202},9,[1153,1204,1205],{},"  to style, theme, or design any web element.\n",[1153,1207,1209],{"class":1155,"line":1208},10,[1153,1210,1159],{},[14,1212,1213],{},"Di bawah frontmatter adalah konten skill yang sebenarnya — instruksi detail yang mencakup aturan warna, aturan tipografi, aturan spacing, spesifikasi komponen, layout/grid, motion, persyaratan aksesibilitas, dan checklist self-review.",[55,1215,1217],{"id":1216},"cara-menggunakannya","Cara menggunakannya",[14,1219,1220],{},"Ada dua cara skill ini aktif:",[14,1222,1223,1226],{},[26,1224,1225],{},"1. Invokasi manual"," — awali prompt dengan nama skill:",[1131,1228,1231],{"className":1229,"code":1230,"language":1136},[1134],"/morpheme-design buat halaman checkout dengan form pengiriman dan ringkasan pesanan\n",[256,1232,1230],{"__ignoreMap":982},[14,1234,1235,1238],{},[26,1236,1237],{},"2. Auto-trigger"," — Claude Code mendeteksi bahwa kamu sedang minta dibuatkan UI dan mengaktifkan skill secara otomatis:",[1131,1240,1243],{"className":1241,"code":1242,"language":1136},[1134],"buatkan halaman settings dengan form profil dan toggle notifikasi\n",[256,1244,1242],{"__ignoreMap":982},[14,1246,1247,1248,1250],{},"Hasilnya sama: Claude membaca ",[256,1249,1120],{},", merencanakan layout, memilih token yang tepat, dan men-generate kode yang mengikuti setiap spec.",[55,1252,1254],{"id":1253},"apa-yang-claude-lakukan","Apa yang Claude lakukan",[14,1256,1257],{},"Saat skill aktif, Claude mengikuti pre-flight checklist:",[145,1259,1260,1266,1269,1272],{},[66,1261,1262,1263,1265],{},"Membaca ",[256,1264,1120],{}," secara lengkap",[66,1267,1268],{},"Mengidentifikasi komponen mana dari DESIGN.md seksi 8.1–8.7 yang dibutuhkan",[66,1270,1271],{},"Merencanakan layout menggunakan sistem grid 12 kolom (§11)",[66,1273,1274],{},"Memilih token tipografi (§3) dan spacing (§4) yang benar",[55,1276,1278],{"id":1277},"struktur-output","Struktur output",[14,1280,1281,1282,1285],{},"Setiap halaman yang di-generate mengikuti struktur CSS 7 lapisan di dalam satu blok ",[256,1283,1284],{},"\u003Cstyle>",":",[1131,1287,1291],{"className":1288,"code":1289,"language":1290,"meta":982,"style":982},"language-html shiki shiki-themes github-light github-dark","\u003Cstyle>\n  /* 1. CSS Reset / Normalize */\n  /* 2. :root variables dari DESIGN.md §14 */\n  /* 3. Base styles */\n  /* 4. Component styles menggunakan var() tokens */\n  /* 5. Layout styles */\n  /* 6. Responsive styles */\n  /* 7. prefers-reduced-motion */\n\u003C/style>\n","html",[256,1292,1293,1306,1312,1317,1322,1327,1332,1337,1342],{"__ignoreMap":982},[1153,1294,1295,1299,1303],{"class":1155,"line":1156},[1153,1296,1298],{"class":1297},"sVt8B","\u003C",[1153,1300,1302],{"class":1301},"s9eBZ","style",[1153,1304,1305],{"class":1297},">\n",[1153,1307,1308],{"class":1155,"line":983},[1153,1309,1311],{"class":1310},"sJ8bj","  /* 1. CSS Reset / Normalize */\n",[1153,1313,1314],{"class":1155,"line":988},[1153,1315,1316],{"class":1310},"  /* 2. :root variables dari DESIGN.md §14 */\n",[1153,1318,1319],{"class":1155,"line":1172},[1153,1320,1321],{"class":1310},"  /* 3. Base styles */\n",[1153,1323,1324],{"class":1155,"line":1178},[1153,1325,1326],{"class":1310},"  /* 4. Component styles menggunakan var() tokens */\n",[1153,1328,1329],{"class":1155,"line":1184},[1153,1330,1331],{"class":1310},"  /* 5. Layout styles */\n",[1153,1333,1334],{"class":1155,"line":1190},[1153,1335,1336],{"class":1310},"  /* 6. Responsive styles */\n",[1153,1338,1339],{"class":1155,"line":1196},[1153,1340,1341],{"class":1310},"  /* 7. prefers-reduced-motion */\n",[1153,1343,1344,1347,1349],{"class":1155,"line":1202},[1153,1345,1346],{"class":1297},"\u003C/",[1153,1348,1302],{"class":1301},[1153,1350,1305],{"class":1297},[14,1352,1353],{},"Penerapan berlapis ini memastikan token selalu didefinisikan sebelum digunakan, responsive override tidak bocor ke base styles, dan preferensi reduced-motion selalu diterapkan terakhir.",[55,1355,1357],{"id":1356},"ikon","Ikon",[14,1359,1360,1361,33],{},"Semua ikon harus menggunakan gaya outline (stroke 1.5–2px). Library yang direkomendasikan: Heroicons, Phosphor Icons, atau Feather Icons. Ukuran: 16px inline, 20px default, 24px untuk header atau ikon fitur. Tombol icon-only wajib menyertakan ",[256,1362,1363],{},"aria-label",[55,1365,1367],{"id":1366},"checklist-self-review","Checklist self-review",[14,1369,1370],{},"Sebelum menampilkan kode final, Claude menjalankan 12 pemeriksaan:",[63,1372,1373,1376,1382,1385,1388,1391,1394,1401,1404,1407,1413,1416],{},[66,1374,1375],{},"Semua warna menggunakan CSS variables",[66,1377,1378,1379],{},"Semua spacing kelipatan 4px via token ",[256,1380,1381],{},"--space-*",[66,1383,1384],{},"Tipografi menggunakan Poppins dengan hierarki weight yang benar",[66,1386,1387],{},"Semua elemen interaktif punya transisi hover (min 100ms)",[66,1389,1390],{},"Focus ring di semua elemen yang bisa difokuskan",[66,1392,1393],{},"Semua form input punya label yang terhubung",[66,1395,1396,1397,1400],{},"Media query ",[256,1398,1399],{},"prefers-reduced-motion"," disertakan",[66,1402,1403],{},"Layout responsif dengan breakpoint yang benar",[66,1405,1406],{},"Shadow sesuai hierarki elevasi",[66,1408,1409,1410],{},"Border-radius menggunakan token ",[256,1411,1412],{},"--radius-*",[66,1414,1415],{},"Sentence case di semua copy",[66,1417,1418],{},"Tidak ada animasi yang memicu layout (hanya opacity/transform)",[38,1420,1422],{"id":1421},"hasilnya-10-contoh-ui-dihasilkan","Hasilnya: 10 contoh UI dihasilkan",[14,1424,1425],{},"Untuk menguji skill ini, saya men-generate 10 contoh UI lengkap — semuanya dari prompt sederhana, semuanya mengikuti design system Morpheme dengan tepat:",[1427,1428,1429,1442],"table",{},[1430,1431,1432],"thead",{},[1433,1434,1435,1439],"tr",{},[1436,1437,1438],"th",{},"Contoh",[1436,1440,1441],{},"Yang dicakup",[1443,1444,1445,1456,1466,1476,1486,1496,1506,1516,1526,1536],"tbody",{},[1433,1446,1447,1453],{},[1448,1449,1450],"td",{},[26,1451,1452],{},"Halaman auth",[1448,1454,1455],{},"Login, register, lupa password, verifikasi OTP",[1433,1457,1458,1463],{},[1448,1459,1460],{},[26,1461,1462],{},"Blog",[1448,1464,1465],{},"Daftar artikel dengan card, detail artikel dengan tipografi prose",[1433,1467,1468,1473],{},[1448,1469,1470],{},[26,1471,1472],{},"CRM admin",[1448,1474,1475],{},"Tabel data kontak, stat card, panel detail slide-over",[1433,1477,1478,1483],{},[1448,1479,1480],{},[26,1481,1482],{},"Dashboard",[1448,1484,1485],{},"Grafik pendapatan, tabel pesanan, feed aktivitas, navigasi sidebar",[1433,1487,1488,1493],{},[1448,1489,1490],{},[26,1491,1492],{},"E-commerce",[1448,1494,1495],{},"Homepage, daftar produk, detail produk, keranjang, checkout (5 halaman)",[1433,1497,1498,1503],{},[1448,1499,1500],{},[26,1501,1502],{},"Email inbox",[1448,1504,1505],{},"Layout 3 panel: sidebar, daftar email, detail pesan dengan lampiran",[1433,1507,1508,1513],{},[1448,1509,1510],{},[26,1511,1512],{},"Halaman error",[1448,1514,1515],{},"404, 500, mode maintenance dengan progress bar",[1433,1517,1518,1523],{},[1448,1519,1520],{},[26,1521,1522],{},"Pricing",[1448,1524,1525],{},"3 tier paket, tabel perbandingan fitur, FAQ accordion",[1433,1527,1528,1533],{},[1448,1529,1530],{},[26,1531,1532],{},"Settings",[1448,1534,1535],{},"Form profil, toggle switch, danger zone, section bertab",[1433,1537,1538,1543],{},[1448,1539,1540],{},[26,1541,1542],{},"SaaS landing",[1448,1544,1545],{},"Hero, fitur, testimoni, section CTA",[14,1547,1548],{},"Setiap halaman:",[63,1550,1551,1554,1557,1562,1565,1568],{},[66,1552,1553],{},"Menggunakan CSS custom properties dari design system",[66,1555,1556],{},"Punya focus ring dan navigasi keyboard yang benar",[66,1558,1559,1560],{},"Menyertakan dukungan ",[256,1561,1399],{},[66,1563,1564],{},"Responsif di mobile, tablet, dan desktop",[66,1566,1567],{},"Mengikuti type scale Poppins dengan tepat",[66,1569,1570],{},"Menggunakan grid spacing 4px",[14,1572,1573,1574,1577],{},"Totalnya ",[26,1575,1576],{},"24 file HTML/CSS",", semuanya sesuai design system, di-generate dari prompt satu baris.",[14,1579,1580],{},[26,1581,1582],{},[18,1583,1585],{"href":1049,"rel":1584},[22],"Lihat semua 10 contoh UI live →",[38,1587,1589],{"id":1588},"cara-install-di-project-kamu","Cara install di project kamu",[55,1591,1593],{"id":1592},"recommended-satu-perintah","Recommended: satu perintah",[1131,1595,1599],{"className":1596,"code":1597,"language":1598,"meta":982,"style":982},"language-bash shiki shiki-themes github-light github-dark","npx skills add https://github.com/gravitano/morpheme-design-skill --skill morpheme-design\n","bash",[256,1600,1601],{"__ignoreMap":982},[1153,1602,1603,1607,1611,1614,1617,1621],{"class":1155,"line":1156},[1153,1604,1606],{"class":1605},"sScJk","npx",[1153,1608,1610],{"class":1609},"sZZnC"," skills",[1153,1612,1613],{"class":1609}," add",[1153,1615,1616],{"class":1609}," https://github.com/gravitano/morpheme-design-skill",[1153,1618,1620],{"class":1619},"sj4cs"," --skill",[1153,1622,1623],{"class":1609}," morpheme-design\n",[55,1625,1627],{"id":1626},"opsi-1-clone-dan-langsung-pakai","Opsi 1: Clone dan langsung pakai",[1131,1629,1631],{"className":1596,"code":1630,"language":1598,"meta":982,"style":982},"git clone https://github.com/gravitano/morpheme-design-skill.git\ncd morpheme-design-skill\nclaude  # skill otomatis terdeteksi\n",[256,1632,1633,1644,1652],{"__ignoreMap":982},[1153,1634,1635,1638,1641],{"class":1155,"line":1156},[1153,1636,1637],{"class":1605},"git",[1153,1639,1640],{"class":1609}," clone",[1153,1642,1643],{"class":1609}," https://github.com/gravitano/morpheme-design-skill.git\n",[1153,1645,1646,1649],{"class":1155,"line":983},[1153,1647,1648],{"class":1619},"cd",[1153,1650,1651],{"class":1609}," morpheme-design-skill\n",[1153,1653,1654,1657],{"class":1155,"line":988},[1153,1655,1656],{"class":1605},"claude",[1153,1658,1659],{"class":1310},"  # skill otomatis terdeteksi\n",[55,1661,1663],{"id":1662},"opsi-2-copy-ke-project-yang-sudah-ada","Opsi 2: Copy ke project yang sudah ada",[1131,1665,1667],{"className":1596,"code":1666,"language":1598,"meta":982,"style":982},"mkdir -p .claude/skills/morpheme-design\ncp /path/to/morpheme-design-skill/.claude/skills/morpheme-design/SKILL.md \\\n   .claude/skills/morpheme-design/\ncp /path/to/morpheme-design-skill/DESIGN.md .\n",[256,1668,1669,1680,1691,1696],{"__ignoreMap":982},[1153,1670,1671,1674,1677],{"class":1155,"line":1156},[1153,1672,1673],{"class":1605},"mkdir",[1153,1675,1676],{"class":1619}," -p",[1153,1678,1679],{"class":1609}," .claude/skills/morpheme-design\n",[1153,1681,1682,1685,1688],{"class":1155,"line":983},[1153,1683,1684],{"class":1605},"cp",[1153,1686,1687],{"class":1609}," /path/to/morpheme-design-skill/.claude/skills/morpheme-design/SKILL.md",[1153,1689,1690],{"class":1619}," \\\n",[1153,1692,1693],{"class":1155,"line":988},[1153,1694,1695],{"class":1609},"   .claude/skills/morpheme-design/\n",[1153,1697,1698,1700,1703],{"class":1155,"line":1172},[1153,1699,1684],{"class":1605},[1153,1701,1702],{"class":1609}," /path/to/morpheme-design-skill/DESIGN.md",[1153,1704,1705],{"class":1609}," .\n",[55,1707,1709],{"id":1708},"opsi-3-install-via-claude-code","Opsi 3: Install via Claude Code",[1131,1711,1714],{"className":1712,"code":1713,"language":1136},[1134],"/install-skill https://github.com/gravitano/morpheme-design-skill\n",[256,1715,1713],{"__ignoreMap":982},[14,1717,1718,1719,1722,1723,1725],{},"Verifikasi dengan ",[256,1720,1721],{},"/skills"," — kamu akan melihat ",[256,1724,402],{}," di daftar.",[38,1727,1729],{"id":1728},"pelajaran-yang-didapat","Pelajaran yang didapat",[55,1731,1733],{"id":1732},"_1-strukturkan-design-system-sebagai-dokumen-referensi-bukan-tutorial","1. Strukturkan design system sebagai dokumen referensi, bukan tutorial",[14,1735,1736,1738],{},[256,1737,1120],{}," diorganisir per kategori (warna, tipografi, spacing, komponen) dengan nilai persis dan code snippet. Ditulis agar machine-readable — tabel, blok kode, pola penamaan yang jelas. Tanpa narasi bertele-tele.",[55,1740,1742],{"id":1741},"_2-file-skill-adalah-instruksi-bukan-dokumentasi","2. File skill adalah instruksi, bukan dokumentasi",[14,1744,1745,1747,1748,1751,1752,1755,1756,1759,1760,1764,1765,1767,1768,1764,1771,33],{},[256,1746,398],{}," memberi tahu Claude ",[26,1749,1750],{},"apa yang harus dilakukan",", bukan apa itu design system. Dia bilang \"selalu gunakan ",[256,1753,1754],{},"var(--color-primary)"," bukan ",[256,1757,1758],{},"#1D6EEB","\" — dia tidak menjelaskan mengapa arsitektur token ada. ",[1761,1762,1763],"em",{},"Mengapa"," ada di ",[256,1766,1120],{},". ",[1761,1769,1770],{},"Bagaimana",[256,1772,398],{},[55,1774,1776],{"id":1775},"_3-checklist-self-review-itu-krusial","3. Checklist self-review itu krusial",[14,1778,1779,1780,1782],{},"Tanpanya, Claude kadang lupa detail kecil — query ",[256,1781,1399],{}," yang hilang, border-radius yang di-hardcode alih-alih pakai token. Checklist di akhir skill berfungsi sebagai gerbang. Claude menjalankannya sebelum menampilkan kode dan menangkap kesalahannya sendiri.",[55,1784,1786],{"id":1785},"_4-skill-harus-mereferensikan-file-bukan-meng-embed-nya","4. Skill harus mereferensikan file, bukan meng-embed-nya",[14,1788,1789,1790,1793],{},"Skill mengatakan ",[256,1791,1792],{},"Read @DESIGN.md"," alih-alih menduplikasi semua token secara inline. Artinya:",[63,1795,1796,1801,1804],{},[66,1797,1798,1800],{},[256,1799,1120],{}," adalah single source of truth",[66,1802,1803],{},"Kamu bisa update token tanpa menyentuh skill",[66,1805,1806],{},"File skill tetap ringkas",[55,1808,1810],{"id":1809},"_5-struktur-direktori-skill-itu-penting","5. Struktur direktori skill itu penting",[14,1812,1813,1814,1817],{},"Awalnya saya membuat skill sebagai ",[256,1815,1816],{},".claude/skills/morpheme-design.md"," — file datar. Tidak berhasil. Claude Code mensyaratkan skill berada dalam subdirektori:",[1131,1819,1822],{"className":1820,"code":1821,"language":1136},[1134],"# Salah\n.claude/skills/morpheme-design.md\n\n# Benar\n.claude/skills/morpheme-design/SKILL.md\n",[256,1823,1821],{"__ignoreMap":982},[14,1825,1826],{},"Detail kecil, tapi bikin saya debugging 10 menit.",[38,1828,1830],{"id":1829},"adaptasi-untuk-design-system-kamu-sendiri","Adaptasi untuk design system kamu sendiri",[14,1832,1833],{},"Pendekatannya generik. Kamu bisa melakukan ini dengan design system apapun:",[145,1835,1836,1845,1855,1865],{},[66,1837,1838,1841,1842,1844],{},[26,1839,1840],{},"Dokumentasikan design system"," dalam file ",[256,1843,1120],{},". Sertakan: token warna, skala tipografi, sistem spacing, spec komponen, aturan aksesibilitas. Gunakan tabel dan blok kode.",[66,1846,1847,1850,1851,1854],{},[26,1848,1849],{},"Buat skill"," di ",[256,1852,1853],{},".claude/skills/nama-skill/SKILL.md",". Sertakan: aturan wajib, spesifikasi komponen, checklist self-review.",[66,1856,1857,1860,1861,1864],{},[26,1858,1859],{},"Referensikan dokumen desain"," dari skill menggunakan ",[256,1862,1863],{},"@DESIGN.md"," — jangan duplikasi konten.",[66,1866,1867,1870],{},[26,1868,1869],{},"Tes dengan prompt yang beragam"," — buat dashboard, form, halaman error. Setiap percobaan mengungkap celah di instruksi skill yang bisa kamu perbaiki.",[14,1872,1873],{},"Insight kuncinya adalah bahwa design system pada dasarnya berbasis aturan. Mereka adalah sekumpulan constraint: pakai warna ini, bukan itu. Pakai spacing ini, bukan nilai sembarang. Selalu sertakan focus ring. Aturan adalah hal yang LLM kuasai — asalkan kamu memberi tahunya dengan cukup jelas.",[38,1875,1877],{"id":1876},"repository","Repository",[14,1879,1880],{},"Skill lengkap, spec design system, dan semua 10 contoh UI bersifat open source:",[14,1882,1883],{},[26,1884,1885],{},[18,1886,1889],{"href":1887,"rel":1888},"https://github.com/gravitano/morpheme-design-skill",[22],"github.com/gravitano/morpheme-design-skill",[14,1891,1892],{},[26,1893,1894],{},[18,1895,1897],{"href":1049,"rel":1896},[22],"Live demo → morpheme-design-md.vercel.app",[35,1899],{},[14,1901,1902],{},[1761,1903,1904],{},"Dibuat dengan Claude Code dan design system Morpheme UI oleh gits.id.",[1302,1906,1907],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":982,"searchDepth":983,"depth":983,"links":1909},[1910,1911,1912,1920,1921,1927,1934,1935],{"id":1056,"depth":983,"text":1057},{"id":1072,"depth":983,"text":1073},{"id":1124,"depth":983,"text":1125,"children":1913},[1914,1915,1916,1917,1918,1919],{"id":1128,"depth":988,"text":1129},{"id":1216,"depth":988,"text":1217},{"id":1253,"depth":988,"text":1254},{"id":1277,"depth":988,"text":1278},{"id":1356,"depth":988,"text":1357},{"id":1366,"depth":988,"text":1367},{"id":1421,"depth":983,"text":1422},{"id":1588,"depth":983,"text":1589,"children":1922},[1923,1924,1925,1926],{"id":1592,"depth":988,"text":1593},{"id":1626,"depth":988,"text":1627},{"id":1662,"depth":988,"text":1663},{"id":1708,"depth":988,"text":1709},{"id":1728,"depth":983,"text":1729,"children":1928},[1929,1930,1931,1932,1933],{"id":1732,"depth":988,"text":1733},{"id":1741,"depth":988,"text":1742},{"id":1775,"depth":988,"text":1776},{"id":1785,"depth":988,"text":1786},{"id":1809,"depth":988,"text":1810},{"id":1829,"depth":983,"text":1830},{"id":1876,"depth":983,"text":1877},"Saya mengemas Morpheme UI sebagai skill Claude Code. Sekarang setiap kali minta Claude bangun halaman, dia otomatis ikuti token, spacing, tipografi, dan aturan aksesibilitas yang benar — tanpa copy-paste spec Figma.",{"date":1938,"keywords":1939,"tags":1940,"slug":1942,"status":1027},"2026-04-15T08:00:00+07:00","Claude Code, Design System, Morpheme UI, Skill, Frontend, AI, CSS Variables, Aksesibilitas",[583,1023,233,1941,1025],"Frontend","skill-claude-code-morpheme-design-system","/images/skill-claude-code-morpheme-design-system.png",{"title":980,"description":1936},"blog/skill-claude-code-morpheme-design-system","hetKyr3gtOLTiw27xd_vOGPQ9EePrbqML38g2fEii8E",{"id":1948,"title":1949,"body":1950,"description":2627,"extension":1017,"meta":2628,"navigation":542,"ogImage":1957,"path":2635,"seo":2636,"stem":2637,"__hash__":2638},"blog/blog/openclaw-telegram-ai-assistant-satu-topik-satu-asisten.md","Saya Bikin 6 AI Assistant di Satu Telegram Group — dan Produktivitas Saya Meledak",{"type":8,"value":1951,"toc":2602},[1952,1958,1962,1967,1970,1992,1998,2000,2004,2019,2026,2029,2107,2114,2116,2120,2124,2127,2131,2134,2154,2158,2171,2173,2177,2181,2223,2226,2228,2248,2252,2297,2315,2329,2337,2341,2362,2368,2388,2394,2396,2400,2404,2407,2427,2437,2441,2444,2462,2465,2471,2474,2478,2481,2487,2490,2496,2498,2502,2506,2512,2516,2524,2528,2534,2538,2544,2546,2550,2557,2560,2565,2568,2570,2579,2599],[14,1953,1954],{},[278,1955],{"alt":1956,"src":1957},"Saya Bikin 6 AI Assistant di Satu Telegram Group","/images/openclaw-telegram-ai-assistant.png",[1959,1960,1949],"h1",{"id":1961},"saya-bikin-6-ai-assistant-di-satu-telegram-group-dan-produktivitas-saya-meledak",[11,1963,1964],{},[14,1965,1966],{},"Satu Telegram Group. Enam topik. Enam asisten dengan peran berbeda. Nol context switching.",[14,1968,1969],{},"Saya punya kebiasaan buruk: tab browser selalu 30+, buka ChatGPT di satu tab, Clockify di tab lain, GitLab di tab lain lagi, Google Calendar entah di mana. Semuanya terpisah, semuanya butuh context switching, dan semuanya menguras fokus.",[14,1971,1972,1973,1976,1977,1980,1981,1984,1985,1988,1989,33],{},"Lalu saya menemukan ",[26,1974,1975],{},"OpenClaw"," — sebuah ",[1761,1978,1979],{},"open-source AI agent framework"," yang bisa dikoneksikan ke berbagai ",[1761,1982,1983],{},"messaging platform",", termasuk Telegram. Dan yang membuat semuanya ",[1761,1986,1987],{},"click"," adalah fitur ",[26,1990,1991],{},"Telegram Group Topics",[14,1993,1994,1995],{},"Ide-nya sederhana: ",[26,1996,1997],{},"satu topik, satu asisten, satu fokus.",[35,1999],{},[38,2001,2003],{"id":2002},"konsep-telegram-group-sebagai-command-center","Konsep: Telegram Group Sebagai Command Center",[14,2005,2006,2007,2010,2011,2014,2015,2018],{},"Telegram Group punya fitur ",[26,2008,2009],{},"Topics"," — semacam ",[1761,2012,2013],{},"thread"," atau ",[1761,2016,2017],{},"channel"," di dalam satu grup. Biasanya fitur ini dipakai di komunitas besar untuk memisahkan pembahasan. Tapi saya melihat potensi lain.",[14,2020,2021,2022,2025],{},"Bagaimana kalau setiap topik punya AI assistant dengan persona dan kemampuan yang ",[26,2023,2024],{},"berbeda","? Bukan satu chatbot generik yang menjawab semuanya, tapi beberapa asisten spesialis yang masing-masing ahli di bidangnya.",[14,2027,2028],{},"Hasilnya, saya setup Telegram Group pribadi dengan topik-topik berikut:",[1427,2030,2031,2041],{},[1430,2032,2033],{},[1433,2034,2035,2038],{},[1436,2036,2037],{},"Topik",[1436,2039,2040],{},"Fungsi",[1443,2042,2043,2053,2063,2073,2083,2097],{},[1433,2044,2045,2050],{},[1448,2046,2047],{},[26,2048,2049],{},"General",[1448,2051,2052],{},"Tanya apa saja — catch-all assistant",[1433,2054,2055,2060],{},[1448,2056,2057],{},[26,2058,2059],{},"Coding",[1448,2061,2062],{},"Semua pertanyaan seputar coding, debugging, arsitektur",[1433,2064,2065,2070],{},[1448,2066,2067],{},[26,2068,2069],{},"Finance",[1448,2071,2072],{},"Financial advisor — analisis, saran, literasi keuangan",[1433,2074,2075,2080],{},[1448,2076,2077],{},[26,2078,2079],{},"Expense Tracker",[1448,2081,2082],{},"Tracking pengeluaran harian — input natural language",[1433,2084,2085,2090],{},[1448,2086,2087],{},[26,2088,2089],{},"Code Review",[1448,2091,2092,2093,2096],{},"Review merge request dari GitLab, terhubung ke ",[256,2094,2095],{},"glab"," CLI",[1433,2098,2099,2104],{},[1448,2100,2101],{},[26,2102,2103],{},"Clockify",[1448,2105,2106],{},"Log waktu kerja otomatis — integrasi GitLab activity + Google Calendar + Clockify API",[14,2108,2109,2110,2113],{},"Setiap topik punya ",[26,2111,2112],{},"system prompt berbeda"," yang mengatur persona, kemampuan, dan batasan masing-masing asisten. Jadi ketika saya chat di topik Finance, asisten-nya tahu bahwa dia adalah financial advisor. Ketika saya chat di Code Review, asisten-nya langsung konteks bahwa dia perlu berinteraksi dengan GitLab.",[35,2115],{},[38,2117,2119],{"id":2118},"yang-bikin-ini-powerful","Yang Bikin Ini Powerful",[55,2121,2123],{"id":2122},"_1-nol-context-switching","1. Nol Context Switching",[14,2125,2126],{},"Semua ada di satu app. Mau cek pengeluaran? Buka topik Expense Tracker. Mau log waktu kerja? Buka topik Clockify. Mau tanya soal TypeScript? Buka topik Coding. Tidak perlu buka browser, tidak perlu buka app lain.",[55,2128,2130],{"id":2129},"_2-setiap-asisten-punya-konteks-yang-tepat","2. Setiap Asisten Punya Konteks yang Tepat",[14,2132,2133],{},"Ini yang membedakan dari punya satu chatbot yang bisa semua. Dengan system prompt terpisah, setiap asisten bisa punya:",[63,2135,2136,2142,2148],{},[66,2137,2138,2141],{},[26,2139,2140],{},"Persona"," yang berbeda (formal untuk finance, santai untuk coding)",[66,2143,2144,2147],{},[26,2145,2146],{},"Tools"," yang berbeda (glab untuk code review, gog untuk clockify)",[66,2149,2150,2153],{},[26,2151,2152],{},"Memory"," yang terfokus (expense tracker hanya ingat transaksi, bukan diskusi coding)",[55,2155,2157],{"id":2156},"_3-integrasi-cli-yang-kuat","3. Integrasi CLI yang Kuat",[14,2159,2160,2161,2163,2164,2167,2168,33],{},"OpenClaw punya kemampuan untuk menjalankan CLI tools. Ini yang membuat integrasi dengan GitLab (",[256,2162,2095],{},"), Google Workspace (",[256,2165,2166],{},"gog","), dan tools lainnya jadi seamless. AI bukan cuma menjawab pertanyaan — dia bisa ",[26,2169,2170],{},"melakukan aksi",[35,2172],{},[38,2174,2176],{"id":2175},"setup-dari-nol-sampai-jalan","Setup: Dari Nol Sampai Jalan",[55,2178,2180],{"id":2179},"step-1-install-openclaw","Step 1: Install OpenClaw",[1131,2182,2184],{"className":1596,"code":2183,"language":1598,"meta":982,"style":982},"# Install OpenClaw\nnpm install -g openclaw\n\n# Jalankan onboarding\nopenclaw onboard\n",[256,2185,2186,2191,2205,2210,2215],{"__ignoreMap":982},[1153,2187,2188],{"class":1155,"line":1156},[1153,2189,2190],{"class":1310},"# Install OpenClaw\n",[1153,2192,2193,2196,2199,2202],{"class":1155,"line":983},[1153,2194,2195],{"class":1605},"npm",[1153,2197,2198],{"class":1609}," install",[1153,2200,2201],{"class":1619}," -g",[1153,2203,2204],{"class":1609}," openclaw\n",[1153,2206,2207],{"class":1155,"line":988},[1153,2208,2209],{"emptyLinePlaceholder":542},"\n",[1153,2211,2212],{"class":1155,"line":1172},[1153,2213,2214],{"class":1310},"# Jalankan onboarding\n",[1153,2216,2217,2220],{"class":1155,"line":1178},[1153,2218,2219],{"class":1605},"openclaw",[1153,2221,2222],{"class":1609}," onboard\n",[14,2224,2225],{},"Proses onboarding akan memandu setup awal: pilih model AI (saya pakai Claude), koneksikan ke Telegram Bot, dan konfigurasi dasar lainnya.",[14,2227,246],{},[63,2229,2230,2236,2242],{},[66,2231,2232,2235],{},[26,2233,2234],{},"Telegram Bot"," — buat via @BotFather, dapatkan token",[66,2237,2238,2241],{},[26,2239,2240],{},"Telegram Group"," — buat group, aktifkan Topics, invite bot sebagai admin",[66,2243,2244,2247],{},[26,2245,2246],{},"API Key"," — untuk model AI yang dipilih (Anthropic, OpenAI, dll.)",[55,2249,2251],{"id":2250},"step-2-setup-glab-cli-gitlab","Step 2: Setup glab CLI (GitLab)",[1131,2253,2255],{"className":1596,"code":2254,"language":1598,"meta":982,"style":982},"# Install glab\nbrew install glab\n\n# Login ke GitLab self-hosted\nglab auth login --hostname git.example.com\n",[256,2256,2257,2262,2272,2276,2281],{"__ignoreMap":982},[1153,2258,2259],{"class":1155,"line":1156},[1153,2260,2261],{"class":1310},"# Install glab\n",[1153,2263,2264,2267,2269],{"class":1155,"line":983},[1153,2265,2266],{"class":1605},"brew",[1153,2268,2198],{"class":1609},[1153,2270,2271],{"class":1609}," glab\n",[1153,2273,2274],{"class":1155,"line":988},[1153,2275,2209],{"emptyLinePlaceholder":542},[1153,2277,2278],{"class":1155,"line":1172},[1153,2279,2280],{"class":1310},"# Login ke GitLab self-hosted\n",[1153,2282,2283,2285,2288,2291,2294],{"class":1155,"line":1178},[1153,2284,2095],{"class":1605},[1153,2286,2287],{"class":1609}," auth",[1153,2289,2290],{"class":1609}," login",[1153,2292,2293],{"class":1619}," --hostname",[1153,2295,2296],{"class":1609}," git.example.com\n",[14,2298,2299,2300,2303,2304,2307,2308,2311,2312,2314],{},"Buat Personal Access Token di GitLab dengan scope ",[256,2301,2302],{},"api",", ",[256,2305,2306],{},"read_repository",", dan ",[256,2309,2310],{},"write_repository",". Setelah login, ",[256,2313,2095],{}," bisa digunakan untuk:",[63,2316,2317,2320,2323,2326],{},[66,2318,2319],{},"List merge requests",[66,2321,2322],{},"Melihat diff dan perubahan",[66,2324,2325],{},"Cek pipeline status",[66,2327,2328],{},"Membaca commit history",[14,2330,2331,2332,2334,2335,33],{},"Ini yang nanti dipakai oleh asisten ",[26,2333,2089],{}," dan ",[26,2336,2103],{},[55,2338,2340],{"id":2339},"step-3-setup-gog-cli-google-workspace","Step 3: Setup gog CLI (Google Workspace)",[1131,2342,2344],{"className":1596,"code":2343,"language":1598,"meta":982,"style":982},"# Install gog\nnpm install -g gog-cli\n",[256,2345,2346,2351],{"__ignoreMap":982},[1153,2347,2348],{"class":1155,"line":1156},[1153,2349,2350],{"class":1310},"# Install gog\n",[1153,2352,2353,2355,2357,2359],{"class":1155,"line":983},[1153,2354,2195],{"class":1605},[1153,2356,2198],{"class":1609},[1153,2358,2201],{"class":1619},[1153,2360,2361],{"class":1609}," gog-cli\n",[14,2363,2364,2365,2367],{},"Untuk ",[256,2366,2166],{},", perlu enable beberapa Google API di Google Cloud Console:",[63,2369,2370,2376,2382],{},[66,2371,2372,2375],{},[26,2373,2374],{},"Google Calendar API"," — untuk baca jadwal dan meeting",[66,2377,2378,2381],{},[26,2379,2380],{},"Google Sheets API"," — untuk simpan data finance/expense",[66,2383,2384,2387],{},[26,2385,2386],{},"Gmail API"," — opsional, untuk integrasi email",[14,2389,2390,2391,2393],{},"Setelah OAuth flow selesai, ",[256,2392,2166],{}," bisa mengakses Google Calendar (dipakai Clockify untuk cross-reference meeting) dan Google Sheets (dipakai Finance/Expense Tracker untuk persistent storage).",[35,2395],{},[38,2397,2399],{"id":2398},"deep-dive-topik-yang-paling-berguna","Deep Dive: Topik yang Paling Berguna",[55,2401,2403],{"id":2402},"code-review-gitlab-merge-request-reviewer","Code Review — GitLab Merge Request Reviewer",[14,2405,2406],{},"Ini mungkin topik yang paling saya suka. Workflow-nya:",[145,2408,2409,2412,2421,2424],{},[66,2410,2411],{},"Saya paste link MR atau ketik \"review MR terbaru\"",[66,2413,2414,2415,2014,2418],{},"Asisten menjalankan ",[256,2416,2417],{},"glab mr list",[256,2419,2420],{},"glab mr view \u003Cid>",[66,2422,2423],{},"Dia membaca diff, memahami perubahan, dan memberikan review",[66,2425,2426],{},"Kalau ada issue, dia suggest perbaikan lengkap dengan code snippet",[14,2428,2429,2430,2432,2433,2436],{},"Karena kerjaan saya mostly di GitLab self-hosted, integrasi ",[256,2431,2095],{}," ini ",[1761,2434,2435],{},"game changer",". Review yang biasanya butuh 15-20 menit bisa dikurangi signifikan karena asisten sudah memberikan summary dan highlight potential issues.",[55,2438,2440],{"id":2439},"clockify-auto-time-logger","Clockify — Auto Time Logger",[14,2442,2443],{},"Topik ini yang paling \"magical\". Saya integrasikan tiga sumber data:",[145,2445,2446,2451,2456],{},[66,2447,2448,2450],{},[26,2449,2095],{}," — untuk cek GitLab activity (commits, merge, push)",[66,2452,2453,2455],{},[26,2454,2166],{}," — untuk cek Google Calendar (meeting, event)",[66,2457,2458,2461],{},[26,2459,2460],{},"Clockify API"," — untuk log waktu kerja",[14,2463,2464],{},"Workflow-nya:",[1131,2466,2469],{"className":2467,"code":2468,"language":1136},[1134],"\"Log waktu kerja hari ini\"\n    ↓\nAsisten cek GitLab activity hari ini (commit, MR)\n    ↓\nAsisten cek Google Calendar (meeting apa saja)\n    ↓\nCross-reference dan compile summary\n    ↓\nLog ke Clockify dengan deskripsi yang sudah ter-format\n",[256,2470,2468],{"__ignoreMap":982},[14,2472,2473],{},"Tidak perlu lagi buka Clockify manual dan mengingat-ingat apa yang dikerjakan hari ini. Asisten sudah punya semua datanya.",[55,2475,2477],{"id":2476},"expense-tracker-natural-language-budgeting","Expense Tracker — Natural Language Budgeting",[14,2479,2480],{},"Tracking pengeluaran jadi semudah chat:",[1131,2482,2485],{"className":2483,"code":2484,"language":1136},[1134],"\"Makan siang 35rb\"\n\"Grab ke kantor 25rb\"\n\"Bayar Netflix 186rb\"\n",[256,2486,2484],{"__ignoreMap":982},[14,2488,2489],{},"Asisten otomatis parsing jumlah, kategori, dan menyimpannya. Mau lihat summary? Tinggal tanya:",[1131,2491,2494],{"className":2492,"code":2493,"language":1136},[1134],"\"Berapa pengeluaran minggu ini?\"\n\"Breakdown per kategori bulan ini\"\n",[256,2495,2493],{"__ignoreMap":982},[35,2497],{},[38,2499,2501],{"id":2500},"pelajaran-yang-dipetik","Pelajaran yang Dipetik",[55,2503,2505],{"id":2504},"_1-system-prompt-itu-segalanya","1. System Prompt Itu Segalanya",[14,2507,2508,2509,33],{},"Perbedaan antara asisten yang berguna dan yang biasa saja ada di system prompt. Semakin spesifik persona dan instruksinya, semakin fokus dan akurat jawabannya. General-purpose AI itu bagus, tapi ",[26,2510,2511],{},"specialist AI jauh lebih powerful untuk task spesifik",[55,2513,2515],{"id":2514},"_2-cli-integration-api-integration","2. CLI Integration > API Integration",[14,2517,2518,2519,2303,2521,2523],{},"Menggunakan CLI tools (",[256,2520,2095],{},[256,2522,2166],{},") jauh lebih pragmatis daripada menulis integrasi API dari nol. CLI sudah handle authentication, pagination, error handling. OpenClaw tinggal menjalankan command dan membaca output-nya.",[55,2525,2527],{"id":2526},"_3-telegram-topics-adalah-hidden-gem","3. Telegram Topics Adalah Hidden Gem",[14,2529,2530,2531,33],{},"Banyak orang tahu Telegram Topics tapi tidak memanfaatkannya. Dengan OpenClaw, Topics berubah dari fitur organisasi chat biasa menjadi ",[26,2532,2533],{},"multi-agent workspace",[55,2535,2537],{"id":2536},"_4-batasan-yang-jelas-bikin-ai-lebih-baik","4. Batasan yang Jelas Bikin AI Lebih Baik",[14,2539,2540,2541],{},"Dengan membatasi setiap asisten pada satu domain, mereka jadi jauh lebih reliable. Asisten Finance tidak akan tiba-tiba ngasih saran coding, asisten Code Review tidak akan mulai tracking expense. ",[26,2542,2543],{},"Constraint creates focus.",[35,2545],{},[38,2547,2549],{"id":2548},"penutup","Penutup",[14,2551,2552,2553,2556],{},"Setup ini mengubah cara saya bekerja sehari-hari. Bukan karena teknologinya canggih — sebenarnya ini hanya kombinasi tools yang sudah ada: Telegram, OpenClaw, CLI tools, dan AI model. Yang membuat perbedaan adalah ",[26,2554,2555],{},"arsitektur-nya",": satu topik, satu asisten, satu fokus.",[14,2558,2559],{},"Kalau Anda seorang developer yang sudah nyaman di terminal dan Telegram, setup ini worth dicoba. Effort-nya relatif kecil (satu malam setup), tapi dampaknya ke daily workflow cukup signifikan.",[11,2561,2562],{},[14,2563,2564],{},"Produktivitas bukan tentang tools yang paling canggih. Tapi tentang mengurangi friction antara niat dan aksi.",[14,2566,2567],{},"Dan saat ini, friction terdekat saya hanyalah membuka Telegram dan mengetik.",[35,2569],{},[14,2571,2572],{},[1761,2573,2574,2575],{},"Mau setup sendiri? Baca tutorial lengkapnya: ",[18,2576,2578],{"href":2577},"/blog/tutorial-setup-openclaw-telegram-ai-assistant","Tutorial: Setup OpenClaw + Telegram dari Nol",[14,2580,2581],{},[1761,2582,2583,2584,2303,2588,2303,2593,2598],{},"Tools yang digunakan: ",[18,2585,1975],{"href":2586,"rel":2587},"https://openclaw.ai",[22],[18,2589,2592],{"href":2590,"rel":2591},"https://gitlab.com/gitlab-org/cli",[22],"glab CLI",[18,2594,2597],{"href":2595,"rel":2596},"https://github.com/nicoa/gog",[22],"gog CLI",", Telegram Bot API, Clockify API.",[1302,2600,2601],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":982,"searchDepth":983,"depth":983,"links":2603},[2604,2605,2610,2615,2620,2626],{"id":2002,"depth":983,"text":2003},{"id":2118,"depth":983,"text":2119,"children":2606},[2607,2608,2609],{"id":2122,"depth":988,"text":2123},{"id":2129,"depth":988,"text":2130},{"id":2156,"depth":988,"text":2157},{"id":2175,"depth":983,"text":2176,"children":2611},[2612,2613,2614],{"id":2179,"depth":988,"text":2180},{"id":2250,"depth":988,"text":2251},{"id":2339,"depth":988,"text":2340},{"id":2398,"depth":983,"text":2399,"children":2616},[2617,2618,2619],{"id":2402,"depth":988,"text":2403},{"id":2439,"depth":988,"text":2440},{"id":2476,"depth":988,"text":2477},{"id":2500,"depth":983,"text":2501,"children":2621},[2622,2623,2624,2625],{"id":2504,"depth":988,"text":2505},{"id":2514,"depth":988,"text":2515},{"id":2526,"depth":988,"text":2527},{"id":2536,"depth":988,"text":2537},{"id":2548,"depth":983,"text":2549},"OpenClaw + Telegram Bot + Telegram Group Topics. Satu topik, satu asisten, satu fokus. Ini cara saya mengubah Telegram jadi command center pribadi.",{"date":2629,"keywords":2630,"tags":2631,"slug":2634,"status":1027},"2026-04-01T08:00:00+07:00","OpenClaw, Telegram Bot, AI Assistant, Productivity, GitLab, Google Calendar, Clockify",[1975,2234,2632,2633],"AI Assistant","Productivity","openclaw-telegram-ai-assistant-satu-topik-satu-asisten","/blog/openclaw-telegram-ai-assistant-satu-topik-satu-asisten",{"title":1949,"description":2627},"blog/openclaw-telegram-ai-assistant-satu-topik-satu-asisten","AInsgg9rWsIypFnGYadk7g4AjIYrFcNWk4lJZqdP4vk",{"id":2640,"title":2641,"body":2642,"description":3915,"extension":1017,"meta":3916,"navigation":542,"ogImage":2648,"path":2577,"seo":3922,"stem":3923,"__hash__":3924},"blog/blog/tutorial-setup-openclaw-telegram-ai-assistant.md","Tutorial: Setup OpenClaw + Telegram dari Nol — Bikin Multi AI Assistant di Satu Group",{"type":8,"value":2643,"toc":3870},[2644,2649,2652,2657,2673,2675,2679,2682,2713,2715,2717,2732,2735,2747,2751,2762,2765,2793,2796,2798,2802,2806,2847,2851,2854,2860,2866,2868,2872,2876,2890,2894,2910,2914,2925,2929,2932,2964,2968,2971,2997,3000,3002,3006,3009,3022,3028,3121,3126,3158,3162,3177,3180,3182,3186,3192,3203,3311,3321,3325,3331,3335,3341,3345,3351,3355,3361,3365,3371,3375,3381,3385,3391,3393,3397,3400,3404,3409,3447,3450,3479,3482,3496,3506,3510,3515,3530,3533,3572,3591,3597,3599,3641,3645,3648,3661,3663,3667,3670,3704,3708,3741,3743,3747,3751,3779,3783,3786,3790,3808,3812,3823,3825,3829,3835,3838,3840,3842,3849,3855,3858,3860,3867],[14,2645,2646],{},[278,2647],{"alt":2578,"src":2648},"/images/tutorial-setup-openclaw-telegram.png",[1959,2650,2641],{"id":2651},"tutorial-setup-openclaw-telegram-dari-nol-bikin-multi-ai-assistant-di-satu-group",[11,2653,2654],{},[14,2655,2656],{},"Dari install sampai punya 6+ AI assistant spesialis di satu Telegram Group. Lengkap dengan contoh system prompt.",[14,2658,2659,2660,2662,2663,2334,2666,2669,2670,33],{},"Artikel ini adalah companion guide dari ",[18,2661,1956],{"href":2635},". Kalau di artikel itu saya cerita ",[1761,2664,2665],{},"apa",[1761,2667,2668],{},"kenapa",", di sini saya tunjukkan ",[1761,2671,2672],{},"bagaimana",[35,2674],{},[38,2676,2678],{"id":2677},"prerequisites","Prerequisites",[14,2680,2681],{},"Sebelum mulai, pastikan sudah punya:",[63,2683,2684,2690,2696,2707],{},[66,2685,2686,2689],{},[26,2687,2688],{},"macOS atau Linux"," (OpenClaw berjalan di local machine)",[66,2691,2692,2695],{},[26,2693,2694],{},"Node.js v18+"," (rekomendasi: gunakan nvm)",[66,2697,2698,2701,2702],{},[26,2699,2700],{},"Anthropic API Key"," — daftar di ",[18,2703,2706],{"href":2704,"rel":2705},"https://console.anthropic.com",[22],"console.anthropic.com",[66,2708,2709,2712],{},[26,2710,2711],{},"Telegram Account"," — untuk membuat bot dan group",[35,2714],{},[38,2716,2180],{"id":2179},[1131,2718,2720],{"className":1596,"code":2719,"language":1598,"meta":982,"style":982},"npm install -g openclaw\n",[256,2721,2722],{"__ignoreMap":982},[1153,2723,2724,2726,2728,2730],{"class":1155,"line":1156},[1153,2725,2195],{"class":1605},[1153,2727,2198],{"class":1609},[1153,2729,2201],{"class":1619},[1153,2731,2204],{"class":1609},[14,2733,2734],{},"Verifikasi instalasi:",[1131,2736,2738],{"className":1596,"code":2737,"language":1598,"meta":982,"style":982},"openclaw --version\n",[256,2739,2740],{"__ignoreMap":982},[1153,2741,2742,2744],{"class":1155,"line":1156},[1153,2743,2219],{"class":1605},[1153,2745,2746],{"class":1619}," --version\n",[55,2748,2750],{"id":2749},"jalankan-onboarding","Jalankan Onboarding",[1131,2752,2754],{"className":1596,"code":2753,"language":1598,"meta":982,"style":982},"openclaw onboard\n",[256,2755,2756],{"__ignoreMap":982},[1153,2757,2758,2760],{"class":1155,"line":1156},[1153,2759,2219],{"class":1605},[1153,2761,2222],{"class":1609},[14,2763,2764],{},"Onboarding wizard akan memandu kamu melalui:",[145,2766,2767,2773,2783],{},[66,2768,2769,2772],{},[26,2770,2771],{},"Setup model"," — pilih provider (Anthropic) dan masukkan API key",[66,2774,2775,2778,2779,2782],{},[26,2776,2777],{},"Setup workspace"," — lokasi file kerja agent (default: ",[256,2780,2781],{},"~/.openclaw/workspace",")",[66,2784,2785,2788,2789,2792],{},[26,2786,2787],{},"Pilih tool profile"," — pilih ",[256,2790,2791],{},"coding"," untuk akses penuh ke CLI tools",[14,2794,2795],{},"Setelah selesai, OpenClaw sudah bisa dipakai via terminal. Tapi kita mau lebih dari itu — kita mau koneksikan ke Telegram.",[35,2797],{},[38,2799,2801],{"id":2800},"step-2-buat-telegram-bot","Step 2: Buat Telegram Bot",[55,2803,2805],{"id":2804},"_21-buat-bot-via-botfather","2.1 Buat Bot via BotFather",[145,2807,2808,2814,2820,2837],{},[66,2809,2810,2811],{},"Buka Telegram, cari ",[26,2812,2813],{},"@BotFather",[66,2815,2816,2817],{},"Kirim ",[256,2818,2819],{},"/newbot",[66,2821,2822,2823],{},"Ikuti instruksi:\n",[63,2824,2825,2831],{},[66,2826,2827,2828,2782],{},"Masukkan nama bot (misal: ",[256,2829,2830],{},"MamangAI",[66,2832,2833,2834,2782],{},"Masukkan username bot (misal: ",[256,2835,2836],{},"mamangclaw_bot",[66,2838,2839,2840,2843,2844],{},"Catat ",[26,2841,2842],{},"Bot Token"," yang diberikan BotFather — format-nya seperti: ",[256,2845,2846],{},"1234567890:ABCdefGhIjKlMnOpQrStUvWxYz",[55,2848,2850],{"id":2849},"_22-konfigurasi-bot-settings","2.2 Konfigurasi Bot Settings",[14,2852,2853],{},"Masih di BotFather, setup beberapa hal:",[1131,2855,2858],{"className":2856,"code":2857,"language":1136},[1134],"/mybots → Pilih bot → Bot Settings → Group Privacy → Turn OFF\n",[256,2859,2857],{"__ignoreMap":982},[14,2861,2862,2865],{},[26,2863,2864],{},"Penting:"," Matikan Group Privacy agar bot bisa membaca semua pesan di group, bukan hanya yang mention bot.",[35,2867],{},[38,2869,2871],{"id":2870},"step-3-buat-telegram-group-dengan-topics","Step 3: Buat Telegram Group dengan Topics",[55,2873,2875],{"id":2874},"_31-buat-group","3.1 Buat Group",[145,2877,2878,2881,2887],{},[66,2879,2880],{},"Buka Telegram → New Group",[66,2882,2883,2884,2782],{},"Beri nama (misal: ",[256,2885,2886],{},"Warsono & MamangAI",[66,2888,2889],{},"Tambahkan bot kamu sebagai member",[55,2891,2893],{"id":2892},"_32-aktifkan-topics","3.2 Aktifkan Topics",[145,2895,2896,2899,2905],{},[66,2897,2898],{},"Buka Group Settings",[66,2900,2901,2902,2904],{},"Scroll ke ",[26,2903,2009],{}," → Aktifkan",[66,2906,2907,2908],{},"Otomatis akan muncul topic ",[26,2909,2049],{},[55,2911,2913],{"id":2912},"_33-buat-bot-sebagai-admin","3.3 Buat Bot sebagai Admin",[145,2915,2916,2919,2922],{},[66,2917,2918],{},"Group Settings → Administrators → Add Administrator",[66,2920,2921],{},"Pilih bot kamu",[66,2923,2924],{},"Berikan permission yang diperlukan (minimal: Read Messages, Send Messages)",[55,2926,2928],{"id":2927},"_34-buat-topics","3.4 Buat Topics",[14,2930,2931],{},"Buat topics sesuai kebutuhan. Contoh setup saya:",[63,2933,2934,2939,2944,2949,2954,2959],{},[66,2935,2936,2938],{},[26,2937,2049],{}," (otomatis ada, ID: 1)",[66,2940,2941,2943],{},[26,2942,2069],{}," — klik New Topic → beri nama \"Finance\"",[66,2945,2946,2948],{},[26,2947,2059],{}," — klik New Topic → beri nama \"Coding\"",[66,2950,2951,2953],{},[26,2952,2079],{}," — klik New Topic → beri nama \"Expense Tracker\"",[66,2955,2956,2958],{},[26,2957,2089],{}," — klik New Topic → beri nama \"Code Review\"",[66,2960,2961,2963],{},[26,2962,2103],{}," — klik New Topic → beri nama \"Clockify\"",[55,2965,2967],{"id":2966},"_35-catat-group-id-dan-topic-ids","3.5 Catat Group ID dan Topic IDs",[14,2969,2970],{},"Untuk mendapatkan Group ID, cara termudah:",[145,2972,2973,2981,2984,2990],{},[66,2974,2975,2976],{},"Buka ",[18,2977,2980],{"href":2978,"rel":2979},"https://web.telegram.org",[22],"web.telegram.org",[66,2982,2983],{},"Buka group kamu",[66,2985,2986,2987],{},"Lihat URL — format-nya: ",[256,2988,2989],{},"https://web.telegram.org/a/#-100XXXXXXXXXX",[66,2991,2992,2993,2996],{},"Angka setelah ",[256,2994,2995],{},"#"," adalah Group ID (termasuk tanda minus)",[14,2998,2999],{},"Untuk Topic ID, klik topic tertentu dan lihat di URL atau bisa juga langsung tanya ke bot setelah terkoneksi nanti.",[35,3001],{},[38,3003,3005],{"id":3004},"step-4-koneksikan-openclaw-ke-telegram","Step 4: Koneksikan OpenClaw ke Telegram",[14,3007,3008],{},"Edit file konfigurasi OpenClaw:",[1131,3010,3012],{"className":1596,"code":3011,"language":1598,"meta":982,"style":982},"nano ~/.openclaw/openclaw.json\n",[256,3013,3014],{"__ignoreMap":982},[1153,3015,3016,3019],{"class":1155,"line":1156},[1153,3017,3018],{"class":1605},"nano",[1153,3020,3021],{"class":1609}," ~/.openclaw/openclaw.json\n",[14,3023,3024,3025,1285],{},"Tambahkan atau edit bagian ",[256,3026,3027],{},"channels.telegram",[1131,3029,3033],{"className":3030,"code":3031,"language":3032,"meta":982,"style":982},"language-json shiki shiki-themes github-light github-dark","{\n  \"channels\": {\n    \"telegram\": {\n      \"enabled\": true,\n      \"botToken\": \"ISI_BOT_TOKEN_DARI_BOTFATHER\",\n      \"dmPolicy\": \"pairing\",\n      \"groupPolicy\": \"allowlist\",\n      \"groups\": {\n        \"-100XXXXXXXXXX\": {\n          \"requireMention\": false,\n          \"groupPolicy\": \"open\"\n        }\n      }\n    }\n  }\n}\n","json",[256,3034,3035,3040,3045,3050,3055,3060,3065,3070,3075,3080,3085,3091,3097,3103,3109,3115],{"__ignoreMap":982},[1153,3036,3037],{"class":1155,"line":1156},[1153,3038,3039],{},"{\n",[1153,3041,3042],{"class":1155,"line":983},[1153,3043,3044],{},"  \"channels\": {\n",[1153,3046,3047],{"class":1155,"line":988},[1153,3048,3049],{},"    \"telegram\": {\n",[1153,3051,3052],{"class":1155,"line":1172},[1153,3053,3054],{},"      \"enabled\": true,\n",[1153,3056,3057],{"class":1155,"line":1178},[1153,3058,3059],{},"      \"botToken\": \"ISI_BOT_TOKEN_DARI_BOTFATHER\",\n",[1153,3061,3062],{"class":1155,"line":1184},[1153,3063,3064],{},"      \"dmPolicy\": \"pairing\",\n",[1153,3066,3067],{"class":1155,"line":1190},[1153,3068,3069],{},"      \"groupPolicy\": \"allowlist\",\n",[1153,3071,3072],{"class":1155,"line":1196},[1153,3073,3074],{},"      \"groups\": {\n",[1153,3076,3077],{"class":1155,"line":1202},[1153,3078,3079],{},"        \"-100XXXXXXXXXX\": {\n",[1153,3081,3082],{"class":1155,"line":1208},[1153,3083,3084],{},"          \"requireMention\": false,\n",[1153,3086,3088],{"class":1155,"line":3087},11,[1153,3089,3090],{},"          \"groupPolicy\": \"open\"\n",[1153,3092,3094],{"class":1155,"line":3093},12,[1153,3095,3096],{},"        }\n",[1153,3098,3100],{"class":1155,"line":3099},13,[1153,3101,3102],{},"      }\n",[1153,3104,3106],{"class":1155,"line":3105},14,[1153,3107,3108],{},"    }\n",[1153,3110,3112],{"class":1155,"line":3111},15,[1153,3113,3114],{},"  }\n",[1153,3116,3118],{"class":1155,"line":3117},16,[1153,3119,3120],{},"}\n",[14,3122,3123],{},[26,3124,3125],{},"Penjelasan:",[63,3127,3128,3134,3140,3146,3152],{},[66,3129,3130,3133],{},[256,3131,3132],{},"botToken"," — token dari BotFather",[66,3135,3136,3139],{},[256,3137,3138],{},"dmPolicy: \"pairing\""," — DM ke bot perlu pairing dulu (security)",[66,3141,3142,3145],{},[256,3143,3144],{},"groupPolicy: \"allowlist\""," — hanya group yang terdaftar yang bisa interact",[66,3147,3148,3151],{},[256,3149,3150],{},"requireMention: false"," — bot merespon semua pesan, tidak perlu di-mention",[66,3153,3154,3157],{},[256,3155,3156],{},"groupPolicy: \"open\""," — semua member group bisa interact dengan bot",[55,3159,3161],{"id":3160},"jalankan-openclaw","Jalankan OpenClaw",[1131,3163,3165],{"className":1596,"code":3164,"language":1598,"meta":982,"style":982},"openclaw gateway start\n",[256,3166,3167],{"__ignoreMap":982},[1153,3168,3169,3171,3174],{"class":1155,"line":1156},[1153,3170,2219],{"class":1605},[1153,3172,3173],{"class":1609}," gateway",[1153,3175,3176],{"class":1609}," start\n",[14,3178,3179],{},"Coba kirim pesan di group — bot seharusnya sudah merespon! 🎉",[35,3181],{},[38,3183,3185],{"id":3184},"step-5-setup-system-prompt-per-topic","Step 5: Setup System Prompt per Topic",[14,3187,3188,3189,33],{},"Ini bagian yang paling seru. Setiap topic bisa punya system prompt berbeda, artinya setiap topic jadi ",[26,3190,3191],{},"asisten spesialis",[14,3193,3194,3195,3198,3199,3202],{},"Edit ",[256,3196,3197],{},"~/.openclaw/openclaw.json",", tambahkan ",[256,3200,3201],{},"topics"," di dalam group config:",[1131,3204,3206],{"className":3030,"code":3205,"language":3032,"meta":982,"style":982},"{\n  \"channels\": {\n    \"telegram\": {\n      \"enabled\": true,\n      \"botToken\": \"ISI_BOT_TOKEN\",\n      \"groups\": {\n        \"-100XXXXXXXXXX\": {\n          \"requireMention\": false,\n          \"groupPolicy\": \"open\",\n          \"topics\": {\n            \"1\": {\n              \"systemPrompt\": \"...\"\n            },\n            \"2\": {\n              \"systemPrompt\": \"...\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n",[256,3207,3208,3212,3216,3220,3224,3229,3233,3237,3241,3246,3251,3256,3261,3266,3271,3275,3280,3286,3291,3296,3301,3306],{"__ignoreMap":982},[1153,3209,3210],{"class":1155,"line":1156},[1153,3211,3039],{},[1153,3213,3214],{"class":1155,"line":983},[1153,3215,3044],{},[1153,3217,3218],{"class":1155,"line":988},[1153,3219,3049],{},[1153,3221,3222],{"class":1155,"line":1172},[1153,3223,3054],{},[1153,3225,3226],{"class":1155,"line":1178},[1153,3227,3228],{},"      \"botToken\": \"ISI_BOT_TOKEN\",\n",[1153,3230,3231],{"class":1155,"line":1184},[1153,3232,3074],{},[1153,3234,3235],{"class":1155,"line":1190},[1153,3236,3079],{},[1153,3238,3239],{"class":1155,"line":1196},[1153,3240,3084],{},[1153,3242,3243],{"class":1155,"line":1202},[1153,3244,3245],{},"          \"groupPolicy\": \"open\",\n",[1153,3247,3248],{"class":1155,"line":1208},[1153,3249,3250],{},"          \"topics\": {\n",[1153,3252,3253],{"class":1155,"line":3087},[1153,3254,3255],{},"            \"1\": {\n",[1153,3257,3258],{"class":1155,"line":3093},[1153,3259,3260],{},"              \"systemPrompt\": \"...\"\n",[1153,3262,3263],{"class":1155,"line":3099},[1153,3264,3265],{},"            },\n",[1153,3267,3268],{"class":1155,"line":3105},[1153,3269,3270],{},"            \"2\": {\n",[1153,3272,3273],{"class":1155,"line":3111},[1153,3274,3260],{},[1153,3276,3277],{"class":1155,"line":3117},[1153,3278,3279],{},"            }\n",[1153,3281,3283],{"class":1155,"line":3282},17,[1153,3284,3285],{},"          }\n",[1153,3287,3289],{"class":1155,"line":3288},18,[1153,3290,3096],{},[1153,3292,3294],{"class":1155,"line":3293},19,[1153,3295,3102],{},[1153,3297,3299],{"class":1155,"line":3298},20,[1153,3300,3108],{},[1153,3302,3304],{"class":1155,"line":3303},21,[1153,3305,3114],{},[1153,3307,3309],{"class":1155,"line":3308},22,[1153,3310,3120],{},[14,3312,3313,3314,3316,3317,3320],{},"Key di ",[256,3315,3201],{}," adalah ",[26,3318,3319],{},"Topic ID"," (string). Berikut contoh system prompt yang saya gunakan:",[55,3322,3324],{"id":3323},"general-topic-id-1","General (Topic ID: 1)",[1131,3326,3329],{"className":3327,"code":3328,"language":1136},[1134],"Ini adalah topic General. Kamu bisa membahas apa saja di sini.\nJawab dengan santai dan helpful.\n",[256,3330,3328],{"__ignoreMap":982},[55,3332,3334],{"id":3333},"finance-topic-id-2","Finance (Topic ID: 2)",[1131,3336,3339],{"className":3337,"code":3338,"language":1136},[1134],"Ini adalah topic Finance. Fokus pada pembahasan keuangan:\nbudgeting, investasi, saving, crypto, saham, financial planning,\ndan topik finansial lainnya. Berikan analisis dan saran yang practical.\n",[256,3340,3338],{"__ignoreMap":982},[55,3342,3344],{"id":3343},"coding-topic-id-14","Coding (Topic ID: 14)",[1131,3346,3349],{"className":3347,"code":3348,"language":1136},[1134],"Ini adalah topic Coding. Fokus pada programming, software development,\ndebugging, code review, dan topik teknis lainnya. Berikan code examples\nyang jelas dan penjelasan yang mudah dipahami.\n",[256,3350,3348],{"__ignoreMap":982},[55,3352,3354],{"id":3353},"expense-tracker-topic-id-100","Expense Tracker (Topic ID: 100)",[1131,3356,3359],{"className":3357,"code":3358,"language":1136},[1134],"Ini adalah topic Expense Tracker. Bantu user mencatat dan melacak\npengeluaran harian. Ketika user menyebut pengeluaran (misal 'makan siang\n35rb', 'grab 25k', 'kopi 18.000'), catat dengan rapi. Bisa juga diminta\nrekap harian/mingguan/bulanan, kategorisasi pengeluaran (makan, transport,\nhiburan, dll), dan analisis pola spending. Format pencatatan yang konsisten\ndan mudah dibaca.\n",[256,3360,3358],{"__ignoreMap":982},[55,3362,3364],{"id":3363},"code-review-topic-id-42","Code Review (Topic ID: 42)",[1131,3366,3369],{"className":3367,"code":3368,"language":1136},[1134],"Ini adalah topic Code Review untuk review Pull Request / Merge Request.\nKamu punya akses ke GitLab CLI (glab) yang terkoneksi ke git.gits.id.\nKetika user memberikan link MR atau menyebut MR number, gunakan\n`glab mr view \u003Cnumber> --repo \u003Cproject>` dan\n`glab mr diff \u003Cnumber> --repo \u003Cproject>` untuk fetch detail dan diff-nya.\n\nReview mencakup:\n1) Bug atau error potensial\n2) Security issues\n3) Performance concerns\n4) Code style dan best practices\n5) Saran improvement\n\nGunakan severity: 🔴 Critical, 🟡 Warning, 💡 Suggestion.\n",[256,3370,3368],{"__ignoreMap":982},[55,3372,3374],{"id":3373},"calendar-topic-id-141","Calendar (Topic ID: 141)",[1131,3376,3379],{"className":3377,"code":3378,"language":1136},[1134],"Ini adalah topic Calendar. Fokus sebagai asisten kalender dan jadwal kerja.\nKamu terkoneksi ke Google Calendar (personal & work) via gog CLI.\n\nBantu user untuk:\n1) Cek jadwal hari ini/besok/minggu ini\n2) Buat event/meeting baru\n3) Update atau cancel event\n4) Set reminder untuk meeting penting\n5) Cek conflict jadwal antar calendar\n6) Rekap agenda harian/mingguan\n\nSelalu konfirmasi sebelum membuat atau mengubah event.\nFormat waktu pakai WIB (Asia/Jakarta).\n",[256,3380,3378],{"__ignoreMap":982},[55,3382,3384],{"id":3383},"email-topic-id-158","Email (Topic ID: 158)",[1131,3386,3389],{"className":3387,"code":3388,"language":1136},[1134],"Ini adalah topic Email. Kamu terkoneksi ke Gmail (personal & work)\nvia gog CLI.\n\nBantu user untuk:\n1) Cek inbox dan unread emails\n2) Baca dan summarize email penting\n3) Search email berdasarkan keyword/sender/date\n4) Draft dan kirim email (selalu konfirmasi sebelum send)\n5) Reply atau forward email\n6) Rekap email penting harian\n\nDefault cek work email kecuali diminta spesifik.\nFormat waktu pakai WIB (Asia/Jakarta).\n",[256,3390,3388],{"__ignoreMap":982},[35,3392],{},[38,3394,3396],{"id":3395},"step-6-setup-cli-tools-opsional-tapi-recommended","Step 6: Setup CLI Tools (Opsional tapi Recommended)",[14,3398,3399],{},"System prompt di atas bisa berdiri sendiri, tapi kekuatan sebenarnya muncul ketika OpenClaw terkoneksi ke CLI tools. Berikut beberapa yang saya pakai:",[55,3401,3403],{"id":3402},"_61-glab-cli-gitlab-integration","6.1 glab CLI — GitLab Integration",[14,3405,3406,3408],{},[256,3407,2095],{}," adalah CLI resmi GitLab. Ini yang dipakai oleh topic Code Review.",[1131,3410,3412],{"className":1596,"code":3411,"language":1598,"meta":982,"style":982},"# Install via Homebrew\nbrew install glab\n\n# Login ke GitLab self-hosted\nglab auth login --hostname git.example.com\n",[256,3413,3414,3419,3427,3431,3435],{"__ignoreMap":982},[1153,3415,3416],{"class":1155,"line":1156},[1153,3417,3418],{"class":1310},"# Install via Homebrew\n",[1153,3420,3421,3423,3425],{"class":1155,"line":983},[1153,3422,2266],{"class":1605},[1153,3424,2198],{"class":1609},[1153,3426,2271],{"class":1609},[1153,3428,3429],{"class":1155,"line":988},[1153,3430,2209],{"emptyLinePlaceholder":542},[1153,3432,3433],{"class":1155,"line":1172},[1153,3434,2280],{"class":1310},[1153,3436,3437,3439,3441,3443,3445],{"class":1155,"line":1178},[1153,3438,2095],{"class":1605},[1153,3440,2287],{"class":1609},[1153,3442,2290],{"class":1609},[1153,3444,2293],{"class":1619},[1153,3446,2296],{"class":1609},[14,3448,3449],{},"Saat login:",[145,3451,3452,3459],{},[66,3453,3454,3455,3458],{},"Pilih ",[26,3456,3457],{},"Token"," sebagai authentication method",[66,3460,3461,3462],{},"Buat Personal Access Token di GitLab:\n",[63,3463,3464,3467,3476],{},[66,3465,3466],{},"Settings → Access Tokens → New Token",[66,3468,3469,3470,2303,3472,2303,3474],{},"Scope: ",[256,3471,2302],{},[256,3473,2306],{},[256,3475,2310],{},[66,3477,3478],{},"Copy token dan paste di terminal",[14,3480,3481],{},"Verifikasi:",[1131,3483,3485],{"className":1596,"code":3484,"language":1598,"meta":982,"style":982},"glab auth status\n",[256,3486,3487],{"__ignoreMap":982},[1153,3488,3489,3491,3493],{"class":1155,"line":1156},[1153,3490,2095],{"class":1605},[1153,3492,2287],{"class":1609},[1153,3494,3495],{"class":1609}," status\n",[14,3497,3498,3499,2303,3502,3505],{},"Sekarang topic Code Review bisa menjalankan ",[256,3500,3501],{},"glab mr view",[256,3503,3504],{},"glab mr diff",", dan command lainnya secara langsung.",[55,3507,3509],{"id":3508},"_62-gog-cli-google-workspace-integration","6.2 gog CLI — Google Workspace Integration",[14,3511,3512,3514],{},[256,3513,2166],{}," memungkinkan OpenClaw mengakses Google Calendar, Gmail, Google Sheets, dan lainnya.",[1131,3516,3518],{"className":1596,"code":3517,"language":1598,"meta":982,"style":982},"npm install -g gog-cli\n",[256,3519,3520],{"__ignoreMap":982},[1153,3521,3522,3524,3526,3528],{"class":1155,"line":1156},[1153,3523,2195],{"class":1605},[1153,3525,2198],{"class":1609},[1153,3527,2201],{"class":1619},[1153,3529,2361],{"class":1609},[14,3531,3532],{},"Setup OAuth:",[145,3534,3535,3543,3563,3566],{},[66,3536,3537,3538],{},"Buat project di ",[18,3539,3542],{"href":3540,"rel":3541},"https://console.cloud.google.com",[22],"Google Cloud Console",[66,3544,3545,3546],{},"Enable API yang dibutuhkan:\n",[63,3547,3548,3553,3558],{},[66,3549,3550,3552],{},[26,3551,2374],{}," — untuk topic Calendar",[66,3554,3555,3557],{},[26,3556,2386],{}," — untuk topic Email",[66,3559,3560,3562],{},[26,3561,2380],{}," — untuk topic Finance/Expense Tracker (data storage)",[66,3564,3565],{},"Buat OAuth 2.0 Client ID (Desktop App)",[66,3567,3568,3569],{},"Download ",[256,3570,3571],{},"credentials.json",[1131,3573,3575],{"className":1596,"code":3574,"language":1598,"meta":982,"style":982},"# Jalankan auth flow\ngog auth login\n",[256,3576,3577,3582],{"__ignoreMap":982},[1153,3578,3579],{"class":1155,"line":1156},[1153,3580,3581],{"class":1310},"# Jalankan auth flow\n",[1153,3583,3584,3586,3588],{"class":1155,"line":983},[1153,3585,2166],{"class":1605},[1153,3587,2287],{"class":1609},[1153,3589,3590],{"class":1609}," login\n",[14,3592,3593,3594,3596],{},"Browser akan terbuka untuk OAuth consent. Setelah authorize, ",[256,3595,2166],{}," siap dipakai.",[14,3598,3481],{},[1131,3600,3602],{"className":1596,"code":3601,"language":1598,"meta":982,"style":982},"# Cek calendar events\ngog calendar list\n\n# Cek email\ngog gmail list --unread\n",[256,3603,3604,3609,3619,3623,3628],{"__ignoreMap":982},[1153,3605,3606],{"class":1155,"line":1156},[1153,3607,3608],{"class":1310},"# Cek calendar events\n",[1153,3610,3611,3613,3616],{"class":1155,"line":983},[1153,3612,2166],{"class":1605},[1153,3614,3615],{"class":1609}," calendar",[1153,3617,3618],{"class":1609}," list\n",[1153,3620,3621],{"class":1155,"line":988},[1153,3622,2209],{"emptyLinePlaceholder":542},[1153,3624,3625],{"class":1155,"line":1172},[1153,3626,3627],{"class":1310},"# Cek email\n",[1153,3629,3630,3632,3635,3638],{"class":1155,"line":1178},[1153,3631,2166],{"class":1605},[1153,3633,3634],{"class":1609}," gmail",[1153,3636,3637],{"class":1609}," list",[1153,3639,3640],{"class":1619}," --unread\n",[55,3642,3644],{"id":3643},"_63-clockify-cli-opsional","6.3 Clockify CLI (Opsional)",[14,3646,3647],{},"Untuk integrasi Clockify (time tracking):",[145,3649,3650,3658],{},[66,3651,3652,3653],{},"Dapatkan API key dari ",[18,3654,3657],{"href":3655,"rel":3656},"https://app.clockify.me/user/settings",[22],"Clockify Settings",[66,3659,3660],{},"Konfigurasi di workspace OpenClaw",[35,3662],{},[38,3664,3666],{"id":3665},"step-7-restart-dan-test","Step 7: Restart dan Test",[14,3668,3669],{},"Setelah semua konfigurasi selesai:",[1131,3671,3673],{"className":1596,"code":3672,"language":1598,"meta":982,"style":982},"# Restart OpenClaw gateway\nopenclaw gateway restart\n\n# Cek status\nopenclaw status\n",[256,3674,3675,3680,3689,3693,3698],{"__ignoreMap":982},[1153,3676,3677],{"class":1155,"line":1156},[1153,3678,3679],{"class":1310},"# Restart OpenClaw gateway\n",[1153,3681,3682,3684,3686],{"class":1155,"line":983},[1153,3683,2219],{"class":1605},[1153,3685,3173],{"class":1609},[1153,3687,3688],{"class":1609}," restart\n",[1153,3690,3691],{"class":1155,"line":988},[1153,3692,2209],{"emptyLinePlaceholder":542},[1153,3694,3695],{"class":1155,"line":1172},[1153,3696,3697],{"class":1310},"# Cek status\n",[1153,3699,3700,3702],{"class":1155,"line":1178},[1153,3701,2219],{"class":1605},[1153,3703,3495],{"class":1609},[55,3705,3707],{"id":3706},"test-setiap-topic","Test setiap topic:",[145,3709,3710,3715,3720,3725,3730,3735],{},[66,3711,3712,3714],{},[26,3713,2049],{}," → Kirim \"Halo!\" — bot harus respon santai",[66,3716,3717,3719],{},[26,3718,2059],{}," → Kirim \"Jelaskan async/await di JavaScript\" — respon fokus coding",[66,3721,3722,3724],{},[26,3723,2069],{}," → Kirim \"Tips budgeting untuk gaji 10 juta\" — respon financial advisor",[66,3726,3727,3729],{},[26,3728,2079],{}," → Kirim \"Makan siang 35rb\" — bot catat pengeluaran",[66,3731,3732,3734],{},[26,3733,2089],{}," → Kirim \"Review MR !123 di project xyz\" — bot fetch dari GitLab",[66,3736,3737,3740],{},[26,3738,3739],{},"Calendar"," → Kirim \"Jadwal hari ini\" — bot baca Google Calendar",[35,3742],{},[38,3744,3746],{"id":3745},"tips-dan-troubleshooting","Tips dan Troubleshooting",[55,3748,3750],{"id":3749},"bot-tidak-merespon-di-group","Bot tidak merespon di group?",[63,3752,3753,3760,3767,3773],{},[66,3754,3755,3756,3759],{},"Pastikan Group Privacy sudah ",[26,3757,3758],{},"OFF"," di BotFather",[66,3761,3762,3763,3766],{},"Pastikan bot adalah ",[26,3764,3765],{},"admin"," di group",[66,3768,3769,3770,2782],{},"Pastikan Group ID benar (termasuk prefix ",[256,3771,3772],{},"-100",[66,3774,3775,3776],{},"Cek log: ",[256,3777,3778],{},"openclaw gateway status",[55,3780,3782],{"id":3781},"topic-id-tidak-ketemu","Topic ID tidak ketemu?",[14,3784,3785],{},"Cara paling mudah: kirim pesan di topic yang ingin kamu cari ID-nya, lalu tanya bot \"berapa topic ID ini?\" — metadata pesan mengandung informasi topic ID.",[55,3787,3789],{"id":3788},"system-prompt-tidak-berpengaruh","System prompt tidak berpengaruh?",[63,3791,3792,3802],{},[66,3793,3794,3795,3798,3799,2782],{},"Pastikan Topic ID di config sesuai (dalam format string: ",[256,3796,3797],{},"\"1\"",", bukan ",[256,3800,3801],{},"1",[66,3803,3804,3805],{},"Restart gateway setelah edit config: ",[256,3806,3807],{},"openclaw gateway restart",[55,3809,3811],{"id":3810},"rate-limit-api-error","Rate limit / API error?",[63,3813,3814,3820],{},[66,3815,3816,3817],{},"Cek sisa quota Anthropic API di ",[18,3818,2706],{"href":2704,"rel":3819},[22],[66,3821,3822],{},"Pertimbangkan gunakan model yang lebih murah untuk topic General (misal Haiku)",[35,3824],{},[38,3826,3828],{"id":3827},"arsitektur-final","Arsitektur Final",[1131,3830,3833],{"className":3831,"code":3832,"language":1136},[1134],"┌─────────────────────────────────────────┐\n│          Telegram Group (Topics)         │\n├──────────┬──────────┬──────────┬────────┤\n│ General  │ Finance  │ Coding   │ ...    │\n│ Topic #1 │ Topic #2 │ Topic #14│        │\n└────┬─────┴────┬─────┴────┬─────┴────┬───┘\n     │          │          │          │\n     └──────────┴──────────┴──────────┘\n                     │\n              ┌──────┴──────┐\n              │   Telegram   │\n              │   Bot API    │\n              └──────┬──────┘\n                     │\n              ┌──────┴──────┐\n              │   OpenClaw   │\n              │   Gateway    │\n              └──────┬──────┘\n                     │\n         ┌───────────┼───────────┐\n         │           │           │\n    ┌────┴────┐ ┌────┴────┐ ┌───┴────┐\n    │ Claude  │ │  glab   │ │  gog   │\n    │  (LLM)  │ │ (GitLab)│ │(Google)│\n    └─────────┘ └─────────┘ └────────┘\n",[256,3834,3832],{"__ignoreMap":982},[14,3836,3837],{},"Setiap topic punya system prompt sendiri → OpenClaw routing berdasarkan Topic ID → AI merespon dengan konteks dan tools yang sesuai.",[35,3839],{},[38,3841,2549],{"id":2548},[14,3843,3844,3845,3848],{},"Keseluruhan setup ini bisa selesai dalam ",[26,3846,3847],{},"1-2 jam"," kalau sudah familiar dengan terminal dan API setup. Yang paling memakan waktu biasanya adalah crafting system prompt yang tepat — tapi itu juga yang paling fun.",[14,3850,3851,3852,3854],{},"Setelah semua jalan, kamu punya ",[26,3853,2533],{}," di Telegram yang bisa diakses dari mana saja — laptop, HP, tablet. No context switching, no extra apps.",[14,3856,3857],{},"Selamat mencoba! 🚀",[35,3859],{},[14,3861,3862],{},[1761,3863,3864,3865],{},"Baca juga: ",[18,3866,1949],{"href":2635},[1302,3868,3869],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":982,"searchDepth":983,"depth":983,"links":3871},[3872,3873,3876,3880,3887,3890,3899,3904,3907,3913,3914],{"id":2677,"depth":983,"text":2678},{"id":2179,"depth":983,"text":2180,"children":3874},[3875],{"id":2749,"depth":988,"text":2750},{"id":2800,"depth":983,"text":2801,"children":3877},[3878,3879],{"id":2804,"depth":988,"text":2805},{"id":2849,"depth":988,"text":2850},{"id":2870,"depth":983,"text":2871,"children":3881},[3882,3883,3884,3885,3886],{"id":2874,"depth":988,"text":2875},{"id":2892,"depth":988,"text":2893},{"id":2912,"depth":988,"text":2913},{"id":2927,"depth":988,"text":2928},{"id":2966,"depth":988,"text":2967},{"id":3004,"depth":983,"text":3005,"children":3888},[3889],{"id":3160,"depth":988,"text":3161},{"id":3184,"depth":983,"text":3185,"children":3891},[3892,3893,3894,3895,3896,3897,3898],{"id":3323,"depth":988,"text":3324},{"id":3333,"depth":988,"text":3334},{"id":3343,"depth":988,"text":3344},{"id":3353,"depth":988,"text":3354},{"id":3363,"depth":988,"text":3364},{"id":3373,"depth":988,"text":3374},{"id":3383,"depth":988,"text":3384},{"id":3395,"depth":983,"text":3396,"children":3900},[3901,3902,3903],{"id":3402,"depth":988,"text":3403},{"id":3508,"depth":988,"text":3509},{"id":3643,"depth":988,"text":3644},{"id":3665,"depth":983,"text":3666,"children":3905},[3906],{"id":3706,"depth":988,"text":3707},{"id":3745,"depth":983,"text":3746,"children":3908},[3909,3910,3911,3912],{"id":3749,"depth":988,"text":3750},{"id":3781,"depth":988,"text":3782},{"id":3788,"depth":988,"text":3789},{"id":3810,"depth":988,"text":3811},{"id":3827,"depth":983,"text":3828},{"id":2548,"depth":983,"text":2549},"Step-by-step lengkap setup OpenClaw dengan Claude, Telegram Bot, Telegram Group Topics, sampai punya beberapa AI assistant spesialis di satu group chat.",{"date":2629,"keywords":3917,"tags":3918,"slug":3921,"status":1027},"OpenClaw, Telegram Bot, Tutorial, Setup, AI Assistant, Claude, glab, gog",[1975,2234,3919,2632,3920],"Tutorial","Claude","tutorial-setup-openclaw-telegram-ai-assistant",{"title":2641,"description":3915},"blog/tutorial-setup-openclaw-telegram-ai-assistant","wev6_izvISzk3UFI0t5p4loMa1f03i8HY6HLgsTmOvA",{"id":3926,"title":3927,"body":3928,"description":3945,"extension":1017,"meta":5933,"navigation":542,"ogImage":3934,"path":5941,"seo":5942,"stem":5943,"__hash__":5944},"blog/blog/ai-chat-database-assistant.md","Membangun AI Chat to Database: Natural Language ke SQL dengan Dual-LLM Pipeline",{"type":8,"value":3929,"toc":5903},[3930,3938,3941,3946,3956,3966,3968,3972,3975,3985,3992,4003,4005,4009,4016,4020,4030,4040,4043,4059,4062,4066,4072,4074,4078,4082,4085,4382,4386,4389,4488,4502,4506,4509,4724,4731,4735,4738,4840,4854,4856,4860,4863,4867,5142,5146,5180,5184,5198,5205,5207,5211,5215,5222,5476,5482,5486,5492,5635,5638,5642,5661,5663,5667,5670,5696,5698,5702,5706,5732,5736,5762,5764,5768,5874,5876,5878,5889,5892,5900],[14,3931,3932],{},[278,3933],{"src":3934,"alt":3935,"className":3936,"style":3937},"/images/ai-chat-db.png","AI Chat DB",[539],"view-transition-name: blog-post-image-introducing-nuxia;",[1959,3939,3927],{"id":3940},"membangun-ai-chat-to-database-natural-language-ke-sql-dengan-dual-llm-pipeline",[11,3942,3943],{},[14,3944,3945],{},"Bagaimana saya membangun POC yang memungkinkan tim bisnis query database menggunakan bahasa natural, tanpa menulis SQL — dan mengapa arsitekturnya menggunakan dua kali LLM call.",[14,3947,3948],{},[1761,3949,3950,3951,33],{},"This article is also available in ",[18,3952,3955],{"href":3953,"rel":3954},"https://medium.com/@gravitano",[22],"English on Medium",[14,3957,3958,214,3961],{},[26,3959,3960],{},"Live Demo:",[18,3962,3965],{"href":3963,"rel":3964},"https://ai-chat-db-app.warsono.dev/",[22],"ai-chat-db-app.warsono.dev",[35,3967],{},[38,3969,3971],{"id":3970},"masalah-yang-ingin-diselesaikan","Masalah yang Ingin Diselesaikan",[14,3973,3974],{},"Di banyak organisasi, data ada di database — tapi akses ke data itu terbatas pada orang-orang yang bisa menulis SQL. Tim sales ingin tahu produk terlaris bulan ini? Buat ticket ke tim data. Tim finance ingin tahu total revenue per kategori? Tunggu report mingguan.",[14,3976,3977,3978,3981,3982],{},"Saya membangun ",[26,3979,3980],{},"AI Chat Database Assistant"," sebagai POC untuk menjawab pertanyaan sederhana: ",[26,3983,3984],{},"bagaimana jika tim bisnis bisa langsung bertanya ke database menggunakan bahasa sehari-hari?",[14,3986,3987,3988,3991],{},"User cukup mengetik: ",[1761,3989,3990],{},"\"Produk apa yang paling laris bulan ini?\""," — dan sistem akan:",[145,3993,3994,3997,4000],{},[66,3995,3996],{},"Generate SQL yang tepat",[66,3998,3999],{},"Jalankan query di PostgreSQL",[66,4001,4002],{},"Kembalikan jawaban dalam Bahasa Indonesia yang conversational",[35,4004],{},[38,4006,4008],{"id":4007},"arsitektur-dual-llm-call-pipeline","Arsitektur: Dual-LLM Call Pipeline",[14,4010,4011,4012,4015],{},"Keputusan arsitektur paling penting dalam project ini adalah menggunakan ",[26,4013,4014],{},"dua kali LLM call"," untuk setiap pertanyaan user, bukan satu.",[55,4017,4019],{"id":4018},"mengapa-dua-llm-call","Mengapa Dua LLM Call?",[14,4021,4022,4025,4026,4029],{},[26,4023,4024],{},"LLM Call #1 — SQL Generation (non-streaming)","\nLLM pertama menerima pertanyaan user + database schema, lalu menghasilkan SQL query. Call ini ",[1761,4027,4028],{},"tidak bisa streaming"," — kita butuh SQL yang lengkap sebelum bisa menjalankan query.",[14,4031,4032,4035,4036,4039],{},[26,4033,4034],{},"LLM Call #2 — Answer Generation (streaming)","\nSetelah query dijalankan dan hasilnya didapat, LLM kedua menerima pertanyaan + SQL + hasil query, lalu ",[1761,4037,4038],{},"streaming"," jawaban konversasional ke user via SSE.",[14,4041,4042],{},"Kenapa tidak satu call saja? Karena kedua task ini punya requirement yang berbeda:",[63,4044,4045,4052],{},[66,4046,4047,4048,4051],{},"SQL generation butuh output yang ",[1761,4049,4050],{},"complete"," — tidak ada gunanya streaming SQL setengah jadi",[66,4053,4054,4055,4058],{},"Answer generation ",[1761,4056,4057],{},"benefit dari streaming"," — user langsung melihat jawaban muncul karakter per karakter, membuat UX terasa responsif",[14,4060,4061],{},"Dengan memisahkan dua concern ini, masing-masing prompt juga bisa di-optimize secara independen.",[55,4063,4065],{"id":4064},"request-flow","Request Flow",[1131,4067,4070],{"className":4068,"code":4069,"language":1136},[1134],"User bertanya\n    ↓\n[Frontend] POST /api/chat (SSE)\n    ↓\n[Backend] Ambil schema dari cache (TTL 1 jam)\n    ↓\n[LLM Call #1] Pertanyaan + Schema → SQL\n    ↓\n[Validator] Whitelist · Blacklist · Injection · Auto-LIMIT\n    ↓\n[PostgreSQL] Execute query (read-only user, 10s timeout)\n    ↓\n[SSE Event] data-query-result: SQL + data + timing\n    ↓\n[LLM Call #2] Pertanyaan + SQL + Data → Stream jawaban\n    ↓\n[SSE Events] text-delta: karakter per karakter\n    ↓\nUser melihat jawaban + SQL + tabel data\n",[256,4071,4069],{"__ignoreMap":982},[35,4073],{},[38,4075,4077],{"id":4076},"implementasi-backend-fastapi-anthropic-sdk","Implementasi Backend: FastAPI + Anthropic SDK",[55,4079,4081],{"id":4080},"streaming-service-jantung-dari-pipeline","Streaming Service — Jantung dari Pipeline",[14,4083,4084],{},"Seluruh orchestration terjadi di satu async generator:",[1131,4086,4090],{"className":4087,"code":4088,"language":4089,"meta":982,"style":982},"language-python shiki shiki-themes github-light github-dark","async def event_generator() -> AsyncIterator[str]:\n    # 1. Ambil schema yang sudah di-enrich\n    schema = await schema_cache.get_schema()\n\n    # 2. LLM Call #1: Generate SQL (non-streaming, butuh output lengkap)\n    sql = await llm_service.generate_sql(question, schema)\n\n    # 3. Validasi SQL (multi-layer security)\n    is_valid, result = validate_sql(sql)\n    if not is_valid:\n        yield error_event(result)\n        return\n\n    # 4. Execute query\n    rows, exec_time_ms = await db_service.execute_query(result)\n\n    # 5. Kirim metadata query sebagai custom SSE event\n    yield sse_event({\n        \"type\": \"data-query-result\",\n        \"data\": {\n            \"sql\": result,\n            \"data\": serialize_rows(rows[:100]),\n            \"execution_time_ms\": exec_time_ms,\n            \"total_rows\": len(rows),\n        },\n    })\n\n    # 6. LLM Call #2: Stream jawaban (streaming, untuk UX responsif)\n    async for delta in llm_service.generate_answer_stream(\n        question, result, rows, len(rows)\n    ):\n        yield sse_event({\"type\": \"text-delta\", \"delta\": delta})\n","python",[256,4091,4092,4113,4118,4132,4136,4141,4153,4157,4162,4172,4183,4191,4196,4200,4205,4217,4221,4226,4234,4247,4255,4263,4277,4286,4300,4306,4312,4317,4323,4341,4352,4358],{"__ignoreMap":982},[1153,4093,4094,4098,4101,4104,4107,4110],{"class":1155,"line":1156},[1153,4095,4097],{"class":4096},"szBVR","async",[1153,4099,4100],{"class":4096}," def",[1153,4102,4103],{"class":1605}," event_generator",[1153,4105,4106],{"class":1297},"() -> AsyncIterator[",[1153,4108,4109],{"class":1619},"str",[1153,4111,4112],{"class":1297},"]:\n",[1153,4114,4115],{"class":1155,"line":983},[1153,4116,4117],{"class":1310},"    # 1. Ambil schema yang sudah di-enrich\n",[1153,4119,4120,4123,4126,4129],{"class":1155,"line":988},[1153,4121,4122],{"class":1297},"    schema ",[1153,4124,4125],{"class":4096},"=",[1153,4127,4128],{"class":4096}," await",[1153,4130,4131],{"class":1297}," schema_cache.get_schema()\n",[1153,4133,4134],{"class":1155,"line":1172},[1153,4135,2209],{"emptyLinePlaceholder":542},[1153,4137,4138],{"class":1155,"line":1178},[1153,4139,4140],{"class":1310},"    # 2. LLM Call #1: Generate SQL (non-streaming, butuh output lengkap)\n",[1153,4142,4143,4146,4148,4150],{"class":1155,"line":1184},[1153,4144,4145],{"class":1297},"    sql ",[1153,4147,4125],{"class":4096},[1153,4149,4128],{"class":4096},[1153,4151,4152],{"class":1297}," llm_service.generate_sql(question, schema)\n",[1153,4154,4155],{"class":1155,"line":1190},[1153,4156,2209],{"emptyLinePlaceholder":542},[1153,4158,4159],{"class":1155,"line":1196},[1153,4160,4161],{"class":1310},"    # 3. Validasi SQL (multi-layer security)\n",[1153,4163,4164,4167,4169],{"class":1155,"line":1202},[1153,4165,4166],{"class":1297},"    is_valid, result ",[1153,4168,4125],{"class":4096},[1153,4170,4171],{"class":1297}," validate_sql(sql)\n",[1153,4173,4174,4177,4180],{"class":1155,"line":1208},[1153,4175,4176],{"class":4096},"    if",[1153,4178,4179],{"class":4096}," not",[1153,4181,4182],{"class":1297}," is_valid:\n",[1153,4184,4185,4188],{"class":1155,"line":3087},[1153,4186,4187],{"class":4096},"        yield",[1153,4189,4190],{"class":1297}," error_event(result)\n",[1153,4192,4193],{"class":1155,"line":3093},[1153,4194,4195],{"class":4096},"        return\n",[1153,4197,4198],{"class":1155,"line":3099},[1153,4199,2209],{"emptyLinePlaceholder":542},[1153,4201,4202],{"class":1155,"line":3105},[1153,4203,4204],{"class":1310},"    # 4. Execute query\n",[1153,4206,4207,4210,4212,4214],{"class":1155,"line":3111},[1153,4208,4209],{"class":1297},"    rows, exec_time_ms ",[1153,4211,4125],{"class":4096},[1153,4213,4128],{"class":4096},[1153,4215,4216],{"class":1297}," db_service.execute_query(result)\n",[1153,4218,4219],{"class":1155,"line":3117},[1153,4220,2209],{"emptyLinePlaceholder":542},[1153,4222,4223],{"class":1155,"line":3282},[1153,4224,4225],{"class":1310},"    # 5. Kirim metadata query sebagai custom SSE event\n",[1153,4227,4228,4231],{"class":1155,"line":3288},[1153,4229,4230],{"class":4096},"    yield",[1153,4232,4233],{"class":1297}," sse_event({\n",[1153,4235,4236,4239,4241,4244],{"class":1155,"line":3293},[1153,4237,4238],{"class":1609},"        \"type\"",[1153,4240,937],{"class":1297},[1153,4242,4243],{"class":1609},"\"data-query-result\"",[1153,4245,4246],{"class":1297},",\n",[1153,4248,4249,4252],{"class":1155,"line":3298},[1153,4250,4251],{"class":1609},"        \"data\"",[1153,4253,4254],{"class":1297},": {\n",[1153,4256,4257,4260],{"class":1155,"line":3303},[1153,4258,4259],{"class":1609},"            \"sql\"",[1153,4261,4262],{"class":1297},": result,\n",[1153,4264,4265,4268,4271,4274],{"class":1155,"line":3308},[1153,4266,4267],{"class":1609},"            \"data\"",[1153,4269,4270],{"class":1297},": serialize_rows(rows[:",[1153,4272,4273],{"class":1619},"100",[1153,4275,4276],{"class":1297},"]),\n",[1153,4278,4280,4283],{"class":1155,"line":4279},23,[1153,4281,4282],{"class":1609},"            \"execution_time_ms\"",[1153,4284,4285],{"class":1297},": exec_time_ms,\n",[1153,4287,4289,4292,4294,4297],{"class":1155,"line":4288},24,[1153,4290,4291],{"class":1609},"            \"total_rows\"",[1153,4293,937],{"class":1297},[1153,4295,4296],{"class":1619},"len",[1153,4298,4299],{"class":1297},"(rows),\n",[1153,4301,4303],{"class":1155,"line":4302},25,[1153,4304,4305],{"class":1297},"        },\n",[1153,4307,4309],{"class":1155,"line":4308},26,[1153,4310,4311],{"class":1297},"    })\n",[1153,4313,4315],{"class":1155,"line":4314},27,[1153,4316,2209],{"emptyLinePlaceholder":542},[1153,4318,4320],{"class":1155,"line":4319},28,[1153,4321,4322],{"class":1310},"    # 6. LLM Call #2: Stream jawaban (streaming, untuk UX responsif)\n",[1153,4324,4326,4329,4332,4335,4338],{"class":1155,"line":4325},29,[1153,4327,4328],{"class":4096},"    async",[1153,4330,4331],{"class":4096}," for",[1153,4333,4334],{"class":1297}," delta ",[1153,4336,4337],{"class":4096},"in",[1153,4339,4340],{"class":1297}," llm_service.generate_answer_stream(\n",[1153,4342,4344,4347,4349],{"class":1155,"line":4343},30,[1153,4345,4346],{"class":1297},"        question, result, rows, ",[1153,4348,4296],{"class":1619},[1153,4350,4351],{"class":1297},"(rows)\n",[1153,4353,4355],{"class":1155,"line":4354},31,[1153,4356,4357],{"class":1297},"    ):\n",[1153,4359,4361,4363,4366,4369,4371,4374,4376,4379],{"class":1155,"line":4360},32,[1153,4362,4187],{"class":4096},[1153,4364,4365],{"class":1297}," sse_event({",[1153,4367,4368],{"class":1609},"\"type\"",[1153,4370,937],{"class":1297},[1153,4372,4373],{"class":1609},"\"text-delta\"",[1153,4375,2303],{"class":1297},[1153,4377,4378],{"class":1609},"\"delta\"",[1153,4380,4381],{"class":1297},": delta})\n",[55,4383,4385],{"id":4384},"llm-service-provider-abstraction","LLM Service — Provider Abstraction",[14,4387,4388],{},"Untuk fleksibilitas, saya menggunakan Python Protocol sebagai abstraksi:",[1131,4390,4392],{"className":4087,"code":4391,"language":4089,"meta":982,"style":982},"class LLMService(Protocol):\n    async def generate_sql(self, question: str, schema: str) -> str: ...\n    async def generate_answer_stream(\n        self, question: str, sql: str, data: list[dict], total_rows: int\n    ) -> AsyncIterator[str]: ...\n",[256,4393,4394,4411,4440,4452,4476],{"__ignoreMap":982},[1153,4395,4396,4399,4402,4405,4408],{"class":1155,"line":1156},[1153,4397,4398],{"class":4096},"class",[1153,4400,4401],{"class":1605}," LLMService",[1153,4403,4404],{"class":1297},"(",[1153,4406,4407],{"class":1605},"Protocol",[1153,4409,4410],{"class":1297},"):\n",[1153,4412,4413,4415,4417,4420,4423,4425,4428,4430,4433,4435,4437],{"class":1155,"line":983},[1153,4414,4328],{"class":4096},[1153,4416,4100],{"class":4096},[1153,4418,4419],{"class":1605}," generate_sql",[1153,4421,4422],{"class":1297},"(self, question: ",[1153,4424,4109],{"class":1619},[1153,4426,4427],{"class":1297},", schema: ",[1153,4429,4109],{"class":1619},[1153,4431,4432],{"class":1297},") -> ",[1153,4434,4109],{"class":1619},[1153,4436,937],{"class":1297},[1153,4438,4439],{"class":1619},"...\n",[1153,4441,4442,4444,4446,4449],{"class":1155,"line":988},[1153,4443,4328],{"class":4096},[1153,4445,4100],{"class":4096},[1153,4447,4448],{"class":1605}," generate_answer_stream",[1153,4450,4451],{"class":1297},"(\n",[1153,4453,4454,4457,4459,4462,4464,4467,4470,4473],{"class":1155,"line":1172},[1153,4455,4456],{"class":1297},"        self, question: ",[1153,4458,4109],{"class":1619},[1153,4460,4461],{"class":1297},", sql: ",[1153,4463,4109],{"class":1619},[1153,4465,4466],{"class":1297},", data: list[",[1153,4468,4469],{"class":1619},"dict",[1153,4471,4472],{"class":1297},"], total_rows: ",[1153,4474,4475],{"class":1619},"int\n",[1153,4477,4478,4481,4483,4486],{"class":1155,"line":1178},[1153,4479,4480],{"class":1297},"    ) -> AsyncIterator[",[1153,4482,4109],{"class":1619},[1153,4484,4485],{"class":1297},"]: ",[1153,4487,4439],{"class":1619},[14,4489,4490,4491,4494,4495,4498,4499,33],{},"Dengan dua implementasi: ",[256,4492,4493],{},"AnthropicLLMService"," (Claude) dan ",[256,4496,4497],{},"OpenRouterLLMService"," (OpenAI-compatible API). Switch provider cukup dengan mengubah environment variable ",[256,4500,4501],{},"LLM_PROVIDER",[55,4503,4505],{"id":4504},"sql-generation-prompt-few-shot-examples","SQL Generation Prompt — Few-Shot Examples",[14,4507,4508],{},"Prompt untuk SQL generation menggunakan few-shot examples dalam Bahasa Indonesia:",[1131,4510,4514],{"className":4511,"code":4512,"language":4513,"meta":982,"style":982},"language-sql shiki shiki-themes github-light github-dark","## Contoh\n\nPertanyaan: \"Berapa total penjualan bulan ini?\"\nSQL: SELECT SUM(total) AS total_penjualan FROM sales\n     WHERE sale_date >= DATE_TRUNC('month', CURRENT_DATE)\n     AND status = 'completed'\n\nPertanyaan: \"Produk apa yang paling laris?\"\nSQL: SELECT p.name AS nama_produk, SUM(s.quantity) AS total_terjual\n     FROM sales s JOIN products p ON s.product_id = p.id\n     WHERE s.status = 'completed'\n     GROUP BY p.name ORDER BY total_terjual DESC LIMIT 10\n","sql",[256,4515,4516,4521,4525,4533,4561,4581,4595,4599,4606,4649,4683,4698],{"__ignoreMap":982},[1153,4517,4518],{"class":1155,"line":1156},[1153,4519,4520],{"class":1297},"## Contoh\n",[1153,4522,4523],{"class":1155,"line":983},[1153,4524,2209],{"emptyLinePlaceholder":542},[1153,4526,4527,4530],{"class":1155,"line":988},[1153,4528,4529],{"class":1297},"Pertanyaan: ",[1153,4531,4532],{"class":1609},"\"Berapa total penjualan bulan ini?\"\n",[1153,4534,4535,4538,4540,4543,4546,4549,4552,4555,4558],{"class":1155,"line":1172},[1153,4536,4537],{"class":4096},"SQL",[1153,4539,937],{"class":1297},[1153,4541,4542],{"class":4096},"SELECT",[1153,4544,4545],{"class":1619}," SUM",[1153,4547,4548],{"class":1297},"(total) ",[1153,4550,4551],{"class":4096},"AS",[1153,4553,4554],{"class":1297}," total_penjualan ",[1153,4556,4557],{"class":4096},"FROM",[1153,4559,4560],{"class":1297}," sales\n",[1153,4562,4563,4566,4569,4572,4575,4578],{"class":1155,"line":1178},[1153,4564,4565],{"class":4096},"     WHERE",[1153,4567,4568],{"class":1297}," sale_date ",[1153,4570,4571],{"class":4096},">=",[1153,4573,4574],{"class":1297}," DATE_TRUNC(",[1153,4576,4577],{"class":1609},"'month'",[1153,4579,4580],{"class":1297},", CURRENT_DATE)\n",[1153,4582,4583,4586,4589,4592],{"class":1155,"line":1184},[1153,4584,4585],{"class":4096},"     AND",[1153,4587,4588],{"class":4096}," status",[1153,4590,4591],{"class":4096}," =",[1153,4593,4594],{"class":1609}," 'completed'\n",[1153,4596,4597],{"class":1155,"line":1190},[1153,4598,2209],{"emptyLinePlaceholder":542},[1153,4600,4601,4603],{"class":1155,"line":1196},[1153,4602,4529],{"class":1297},[1153,4604,4605],{"class":1609},"\"Produk apa yang paling laris?\"\n",[1153,4607,4608,4610,4612,4614,4617,4619,4622,4625,4628,4631,4633,4636,4638,4641,4644,4646],{"class":1155,"line":1202},[1153,4609,4537],{"class":4096},[1153,4611,937],{"class":1297},[1153,4613,4542],{"class":4096},[1153,4615,4616],{"class":1619}," p",[1153,4618,33],{"class":1297},[1153,4620,4621],{"class":1619},"name",[1153,4623,4624],{"class":4096}," AS",[1153,4626,4627],{"class":1297}," nama_produk, ",[1153,4629,4630],{"class":1619},"SUM",[1153,4632,4404],{"class":1297},[1153,4634,4635],{"class":1619},"s",[1153,4637,33],{"class":1297},[1153,4639,4640],{"class":1619},"quantity",[1153,4642,4643],{"class":1297},") ",[1153,4645,4551],{"class":4096},[1153,4647,4648],{"class":1297}," total_terjual\n",[1153,4650,4651,4654,4657,4660,4663,4666,4669,4671,4674,4676,4678,4680],{"class":1155,"line":1208},[1153,4652,4653],{"class":4096},"     FROM",[1153,4655,4656],{"class":1297}," sales s ",[1153,4658,4659],{"class":4096},"JOIN",[1153,4661,4662],{"class":1297}," products p ",[1153,4664,4665],{"class":4096},"ON",[1153,4667,4668],{"class":1619}," s",[1153,4670,33],{"class":1297},[1153,4672,4673],{"class":1619},"product_id",[1153,4675,4591],{"class":4096},[1153,4677,4616],{"class":1619},[1153,4679,33],{"class":1297},[1153,4681,4682],{"class":1619},"id\n",[1153,4684,4685,4687,4689,4691,4694,4696],{"class":1155,"line":3087},[1153,4686,4565],{"class":4096},[1153,4688,4668],{"class":1619},[1153,4690,33],{"class":1297},[1153,4692,4693],{"class":1619},"status",[1153,4695,4591],{"class":4096},[1153,4697,4594],{"class":1609},[1153,4699,4700,4703,4705,4707,4709,4712,4715,4718,4721],{"class":1155,"line":3093},[1153,4701,4702],{"class":4096},"     GROUP BY",[1153,4704,4616],{"class":1619},[1153,4706,33],{"class":1297},[1153,4708,4621],{"class":1619},[1153,4710,4711],{"class":4096}," ORDER BY",[1153,4713,4714],{"class":1297}," total_terjual ",[1153,4716,4717],{"class":4096},"DESC",[1153,4719,4720],{"class":4096}," LIMIT",[1153,4722,4723],{"class":1619}," 10\n",[14,4725,4726,4727,4730],{},"Few-shot examples penting karena memberikan pattern yang konsisten — bagaimana menangani \"bulan ini\" (",[256,4728,4729],{},"DATE_TRUNC","), bagaimana join antar tabel, dan bagaimana handle filter status.",[55,4732,4734],{"id":4733},"schema-enrichment-konteks-bisnis-untuk-llm","Schema Enrichment — Konteks Bisnis untuk LLM",[14,4736,4737],{},"Alih-alih mengirim raw database schema ke LLM, saya memperkaya setiap kolom dengan deskripsi dalam Bahasa Indonesia:",[1131,4739,4741],{"className":4087,"code":4740,"language":4089,"meta":982,"style":982},"COLUMN_DESCRIPTIONS = {\n    \"sales\": {\n        \"unit_price\": \"Harga per unit dalam Rupiah\",\n        \"total\": \"Total harga (quantity x unit_price) dalam Rupiah\",\n        \"status\": \"Status transaksi: 'completed', 'pending', 'cancelled'\",\n        \"sale_date\": \"Tanggal transaksi (format: YYYY-MM-DD)\",\n    },\n    \"products\": {\n        \"category\": \"Kategori: Elektronik, Fashion, Makanan, dll.\",\n    },\n}\n",[256,4742,4743,4753,4760,4772,4784,4796,4808,4813,4820,4832,4836],{"__ignoreMap":982},[1153,4744,4745,4748,4750],{"class":1155,"line":1156},[1153,4746,4747],{"class":1619},"COLUMN_DESCRIPTIONS",[1153,4749,4591],{"class":4096},[1153,4751,4752],{"class":1297}," {\n",[1153,4754,4755,4758],{"class":1155,"line":983},[1153,4756,4757],{"class":1609},"    \"sales\"",[1153,4759,4254],{"class":1297},[1153,4761,4762,4765,4767,4770],{"class":1155,"line":988},[1153,4763,4764],{"class":1609},"        \"unit_price\"",[1153,4766,937],{"class":1297},[1153,4768,4769],{"class":1609},"\"Harga per unit dalam Rupiah\"",[1153,4771,4246],{"class":1297},[1153,4773,4774,4777,4779,4782],{"class":1155,"line":1172},[1153,4775,4776],{"class":1609},"        \"total\"",[1153,4778,937],{"class":1297},[1153,4780,4781],{"class":1609},"\"Total harga (quantity x unit_price) dalam Rupiah\"",[1153,4783,4246],{"class":1297},[1153,4785,4786,4789,4791,4794],{"class":1155,"line":1178},[1153,4787,4788],{"class":1609},"        \"status\"",[1153,4790,937],{"class":1297},[1153,4792,4793],{"class":1609},"\"Status transaksi: 'completed', 'pending', 'cancelled'\"",[1153,4795,4246],{"class":1297},[1153,4797,4798,4801,4803,4806],{"class":1155,"line":1184},[1153,4799,4800],{"class":1609},"        \"sale_date\"",[1153,4802,937],{"class":1297},[1153,4804,4805],{"class":1609},"\"Tanggal transaksi (format: YYYY-MM-DD)\"",[1153,4807,4246],{"class":1297},[1153,4809,4810],{"class":1155,"line":1190},[1153,4811,4812],{"class":1297},"    },\n",[1153,4814,4815,4818],{"class":1155,"line":1196},[1153,4816,4817],{"class":1609},"    \"products\"",[1153,4819,4254],{"class":1297},[1153,4821,4822,4825,4827,4830],{"class":1155,"line":1202},[1153,4823,4824],{"class":1609},"        \"category\"",[1153,4826,937],{"class":1297},[1153,4828,4829],{"class":1609},"\"Kategori: Elektronik, Fashion, Makanan, dll.\"",[1153,4831,4246],{"class":1297},[1153,4833,4834],{"class":1155,"line":1208},[1153,4835,4812],{"class":1297},[1153,4837,4838],{"class":1155,"line":3087},[1153,4839,3120],{"class":1297},[14,4841,4842,4843,4846,4847,4850,4851,4853],{},"Schema ini di-cache selama 1 jam dan di-refresh otomatis dari ",[256,4844,4845],{},"information_schema.columns",". Hasilnya, LLM mendapat konteks bisnis yang kaya — tahu bahwa ",[256,4848,4849],{},"total"," adalah Rupiah, tahu enum apa saja yang valid untuk ",[256,4852,4693],{},", dan tahu format tanggal yang dipakai.",[35,4855],{},[38,4857,4859],{"id":4858},"security-defense-in-depth","Security: Defense in Depth",[14,4861,4862],{},"Memberikan LLM akses ke database terdengar berbahaya. Ini benar — dan itulah mengapa security-nya berlapis:",[55,4864,4866],{"id":4865},"layer-1-sql-validator-application-level","Layer 1: SQL Validator (Application Level)",[1131,4868,4870],{"className":4087,"code":4869,"language":4089,"meta":982,"style":982},"def validate_sql(sql: str) -> tuple[bool, str]:\n    # Whitelist: hanya SELECT dan WITH (CTE)\n    if not upper.startswith((\"SELECT\", \"WITH\")):\n        return False, \"Only SELECT queries are allowed\"\n\n    # Blacklist: blokir DML/DDL keywords\n    BLACKLISTED = [\"DROP\", \"DELETE\", \"UPDATE\", \"INSERT\",\n                   \"ALTER\", \"TRUNCATE\", \"GRANT\", \"REVOKE\"]\n\n    # Injection: blokir multiple statements\n    if re.search(r\";\\s*\\S\", sql):\n        return False, \"Multiple SQL statements are not allowed\"\n\n    # Injection: blokir SQL comments\n    if \"--\" in sql:\n        return False, \"SQL comments are not allowed\"\n\n    # Safety net: auto-append LIMIT 1000\n    if \"LIMIT\" not in sql.upper():\n        sql = f\"{sql} LIMIT 1000\"\n\n    return True, sql\n",[256,4871,4872,4897,4902,4922,4935,4939,4944,4974,4997,5001,5006,5037,5048,5052,5057,5070,5081,5085,5090,5104,5127,5131],{"__ignoreMap":982},[1153,4873,4874,4877,4880,4883,4885,4888,4891,4893,4895],{"class":1155,"line":1156},[1153,4875,4876],{"class":4096},"def",[1153,4878,4879],{"class":1605}," validate_sql",[1153,4881,4882],{"class":1297},"(sql: ",[1153,4884,4109],{"class":1619},[1153,4886,4887],{"class":1297},") -> tuple[",[1153,4889,4890],{"class":1619},"bool",[1153,4892,2303],{"class":1297},[1153,4894,4109],{"class":1619},[1153,4896,4112],{"class":1297},[1153,4898,4899],{"class":1155,"line":983},[1153,4900,4901],{"class":1310},"    # Whitelist: hanya SELECT dan WITH (CTE)\n",[1153,4903,4904,4906,4908,4911,4914,4916,4919],{"class":1155,"line":988},[1153,4905,4176],{"class":4096},[1153,4907,4179],{"class":4096},[1153,4909,4910],{"class":1297}," upper.startswith((",[1153,4912,4913],{"class":1609},"\"SELECT\"",[1153,4915,2303],{"class":1297},[1153,4917,4918],{"class":1609},"\"WITH\"",[1153,4920,4921],{"class":1297},")):\n",[1153,4923,4924,4927,4930,4932],{"class":1155,"line":1172},[1153,4925,4926],{"class":4096},"        return",[1153,4928,4929],{"class":1619}," False",[1153,4931,2303],{"class":1297},[1153,4933,4934],{"class":1609},"\"Only SELECT queries are allowed\"\n",[1153,4936,4937],{"class":1155,"line":1178},[1153,4938,2209],{"emptyLinePlaceholder":542},[1153,4940,4941],{"class":1155,"line":1184},[1153,4942,4943],{"class":1310},"    # Blacklist: blokir DML/DDL keywords\n",[1153,4945,4946,4949,4951,4954,4957,4959,4962,4964,4967,4969,4972],{"class":1155,"line":1190},[1153,4947,4948],{"class":1619},"    BLACKLISTED",[1153,4950,4591],{"class":4096},[1153,4952,4953],{"class":1297}," [",[1153,4955,4956],{"class":1609},"\"DROP\"",[1153,4958,2303],{"class":1297},[1153,4960,4961],{"class":1609},"\"DELETE\"",[1153,4963,2303],{"class":1297},[1153,4965,4966],{"class":1609},"\"UPDATE\"",[1153,4968,2303],{"class":1297},[1153,4970,4971],{"class":1609},"\"INSERT\"",[1153,4973,4246],{"class":1297},[1153,4975,4976,4979,4981,4984,4986,4989,4991,4994],{"class":1155,"line":1196},[1153,4977,4978],{"class":1609},"                   \"ALTER\"",[1153,4980,2303],{"class":1297},[1153,4982,4983],{"class":1609},"\"TRUNCATE\"",[1153,4985,2303],{"class":1297},[1153,4987,4988],{"class":1609},"\"GRANT\"",[1153,4990,2303],{"class":1297},[1153,4992,4993],{"class":1609},"\"REVOKE\"",[1153,4995,4996],{"class":1297},"]\n",[1153,4998,4999],{"class":1155,"line":1202},[1153,5000,2209],{"emptyLinePlaceholder":542},[1153,5002,5003],{"class":1155,"line":1208},[1153,5004,5005],{"class":1310},"    # Injection: blokir multiple statements\n",[1153,5007,5008,5010,5013,5016,5019,5023,5026,5029,5032,5034],{"class":1155,"line":3087},[1153,5009,4176],{"class":4096},[1153,5011,5012],{"class":1297}," re.search(",[1153,5014,5015],{"class":4096},"r",[1153,5017,5018],{"class":1609},"\"",[1153,5020,5022],{"class":5021},"sA_wV",";",[1153,5024,5025],{"class":1619},"\\s",[1153,5027,5028],{"class":4096},"*",[1153,5030,5031],{"class":1619},"\\S",[1153,5033,5018],{"class":1609},[1153,5035,5036],{"class":1297},", sql):\n",[1153,5038,5039,5041,5043,5045],{"class":1155,"line":3093},[1153,5040,4926],{"class":4096},[1153,5042,4929],{"class":1619},[1153,5044,2303],{"class":1297},[1153,5046,5047],{"class":1609},"\"Multiple SQL statements are not allowed\"\n",[1153,5049,5050],{"class":1155,"line":3099},[1153,5051,2209],{"emptyLinePlaceholder":542},[1153,5053,5054],{"class":1155,"line":3105},[1153,5055,5056],{"class":1310},"    # Injection: blokir SQL comments\n",[1153,5058,5059,5061,5064,5067],{"class":1155,"line":3111},[1153,5060,4176],{"class":4096},[1153,5062,5063],{"class":1609}," \"--\"",[1153,5065,5066],{"class":4096}," in",[1153,5068,5069],{"class":1297}," sql:\n",[1153,5071,5072,5074,5076,5078],{"class":1155,"line":3117},[1153,5073,4926],{"class":4096},[1153,5075,4929],{"class":1619},[1153,5077,2303],{"class":1297},[1153,5079,5080],{"class":1609},"\"SQL comments are not allowed\"\n",[1153,5082,5083],{"class":1155,"line":3282},[1153,5084,2209],{"emptyLinePlaceholder":542},[1153,5086,5087],{"class":1155,"line":3288},[1153,5088,5089],{"class":1310},"    # Safety net: auto-append LIMIT 1000\n",[1153,5091,5092,5094,5097,5099,5101],{"class":1155,"line":3293},[1153,5093,4176],{"class":4096},[1153,5095,5096],{"class":1609}," \"LIMIT\"",[1153,5098,4179],{"class":4096},[1153,5100,5066],{"class":4096},[1153,5102,5103],{"class":1297}," sql.upper():\n",[1153,5105,5106,5109,5111,5114,5116,5119,5121,5124],{"class":1155,"line":3298},[1153,5107,5108],{"class":1297},"        sql ",[1153,5110,4125],{"class":4096},[1153,5112,5113],{"class":4096}," f",[1153,5115,5018],{"class":1609},[1153,5117,5118],{"class":1619},"{",[1153,5120,4513],{"class":1297},[1153,5122,5123],{"class":1619},"}",[1153,5125,5126],{"class":1609}," LIMIT 1000\"\n",[1153,5128,5129],{"class":1155,"line":3303},[1153,5130,2209],{"emptyLinePlaceholder":542},[1153,5132,5133,5136,5139],{"class":1155,"line":3308},[1153,5134,5135],{"class":4096},"    return",[1153,5137,5138],{"class":1619}," True",[1153,5140,5141],{"class":1297},", sql\n",[55,5143,5145],{"id":5144},"layer-2-database-level","Layer 2: Database Level",[63,5147,5148,5161,5167],{},[66,5149,5150,5153,5154,5157,5158,5160],{},[26,5151,5152],{},"Read-only PostgreSQL user"," — role ",[256,5155,5156],{},"readonly_user"," hanya punya ",[256,5159,4542],{}," grant",[66,5162,5163,5166],{},[26,5164,5165],{},"Statement timeout 10 detik"," — query yang terlalu lama otomatis di-kill",[66,5168,5169,5172,5173,2334,5176,5179],{},[26,5170,5171],{},"Table whitelist"," — hanya ",[256,5174,5175],{},"sales",[256,5177,5178],{},"products"," yang di-expose ke LLM",[55,5181,5183],{"id":5182},"layer-3-application-safeguards","Layer 3: Application Safeguards",[63,5185,5186,5192],{},[66,5187,5188,5191],{},[26,5189,5190],{},"Rate limiting"," — 20 request per IP per menit",[66,5193,5194,5197],{},[26,5195,5196],{},"Audit logging"," — setiap query di-log sebagai structured JSON (timestamp, IP, pertanyaan, SQL, status)",[14,5199,5200,5201,5204],{},"Bahkan jika LLM entah bagaimana menghasilkan ",[256,5202,5203],{},"DROP TABLE"," (yang seharusnya tidak mungkin karena prompt engineering), query tetap akan ditolak oleh validator, dan kalaupun lolos validator, database user tidak punya permission untuk mengeksekusinya.",[35,5206],{},[38,5208,5210],{"id":5209},"frontend-nuxt-4-ai-sdk-v6","Frontend: Nuxt 4 + AI SDK v6",[55,5212,5214],{"id":5213},"chat-singleton-pattern","Chat Singleton Pattern",[14,5216,5217,5218,5221],{},"Saya menggunakan Vercel AI SDK v6 dengan ",[256,5219,5220],{},"@ai-sdk/vue"," untuk menangani streaming. Chat instance dikelola sebagai singleton via Vue composable:",[1131,5223,5227],{"className":5224,"code":5225,"language":5226,"meta":982,"style":982},"language-typescript shiki shiki-themes github-light github-dark","const chat = shallowRef\u003CChat | null>(null)\n\nexport function useChat() {\n  function ensureChat() {\n    if (!chat.value) {\n      chat.value = new Chat({\n        transport: new DefaultChatTransport({\n          api: `${config.public.apiBaseUrl}/api/chat`,\n        }),\n      })\n    }\n    return chat.value\n  }\n\n  function newChat() {\n    chat.value = new Chat({\n      transport: new DefaultChatTransport({\n        api: `${config.public.apiBaseUrl}/api/chat`,\n      }),\n    })\n    return chat.value\n  }\n\n  return { chat, ensureChat, newChat }\n}\n","typescript",[256,5228,5229,5262,5266,5280,5290,5303,5319,5332,5358,5363,5368,5372,5379,5383,5387,5396,5409,5420,5441,5446,5450,5456,5460,5464,5472],{"__ignoreMap":982},[1153,5230,5231,5234,5237,5239,5242,5244,5247,5250,5253,5256,5259],{"class":1155,"line":1156},[1153,5232,5233],{"class":4096},"const",[1153,5235,5236],{"class":1619}," chat",[1153,5238,4591],{"class":4096},[1153,5240,5241],{"class":1605}," shallowRef",[1153,5243,1298],{"class":1297},[1153,5245,5246],{"class":1605},"Chat",[1153,5248,5249],{"class":4096}," |",[1153,5251,5252],{"class":1619}," null",[1153,5254,5255],{"class":1297},">(",[1153,5257,5258],{"class":1619},"null",[1153,5260,5261],{"class":1297},")\n",[1153,5263,5264],{"class":1155,"line":983},[1153,5265,2209],{"emptyLinePlaceholder":542},[1153,5267,5268,5271,5274,5277],{"class":1155,"line":988},[1153,5269,5270],{"class":4096},"export",[1153,5272,5273],{"class":4096}," function",[1153,5275,5276],{"class":1605}," useChat",[1153,5278,5279],{"class":1297},"() {\n",[1153,5281,5282,5285,5288],{"class":1155,"line":1172},[1153,5283,5284],{"class":4096},"  function",[1153,5286,5287],{"class":1605}," ensureChat",[1153,5289,5279],{"class":1297},[1153,5291,5292,5294,5297,5300],{"class":1155,"line":1178},[1153,5293,4176],{"class":4096},[1153,5295,5296],{"class":1297}," (",[1153,5298,5299],{"class":4096},"!",[1153,5301,5302],{"class":1297},"chat.value) {\n",[1153,5304,5305,5308,5310,5313,5316],{"class":1155,"line":1184},[1153,5306,5307],{"class":1297},"      chat.value ",[1153,5309,4125],{"class":4096},[1153,5311,5312],{"class":4096}," new",[1153,5314,5315],{"class":1605}," Chat",[1153,5317,5318],{"class":1297},"({\n",[1153,5320,5321,5324,5327,5330],{"class":1155,"line":1190},[1153,5322,5323],{"class":1297},"        transport: ",[1153,5325,5326],{"class":4096},"new",[1153,5328,5329],{"class":1605}," DefaultChatTransport",[1153,5331,5318],{"class":1297},[1153,5333,5334,5337,5340,5343,5345,5348,5350,5353,5356],{"class":1155,"line":1196},[1153,5335,5336],{"class":1297},"          api: ",[1153,5338,5339],{"class":1609},"`${",[1153,5341,5342],{"class":1297},"config",[1153,5344,33],{"class":1609},[1153,5346,5347],{"class":1297},"public",[1153,5349,33],{"class":1609},[1153,5351,5352],{"class":1297},"apiBaseUrl",[1153,5354,5355],{"class":1609},"}/api/chat`",[1153,5357,4246],{"class":1297},[1153,5359,5360],{"class":1155,"line":1202},[1153,5361,5362],{"class":1297},"        }),\n",[1153,5364,5365],{"class":1155,"line":1208},[1153,5366,5367],{"class":1297},"      })\n",[1153,5369,5370],{"class":1155,"line":3087},[1153,5371,3108],{"class":1297},[1153,5373,5374,5376],{"class":1155,"line":3093},[1153,5375,5135],{"class":4096},[1153,5377,5378],{"class":1297}," chat.value\n",[1153,5380,5381],{"class":1155,"line":3099},[1153,5382,3114],{"class":1297},[1153,5384,5385],{"class":1155,"line":3105},[1153,5386,2209],{"emptyLinePlaceholder":542},[1153,5388,5389,5391,5394],{"class":1155,"line":3111},[1153,5390,5284],{"class":4096},[1153,5392,5393],{"class":1605}," newChat",[1153,5395,5279],{"class":1297},[1153,5397,5398,5401,5403,5405,5407],{"class":1155,"line":3117},[1153,5399,5400],{"class":1297},"    chat.value ",[1153,5402,4125],{"class":4096},[1153,5404,5312],{"class":4096},[1153,5406,5315],{"class":1605},[1153,5408,5318],{"class":1297},[1153,5410,5411,5414,5416,5418],{"class":1155,"line":3282},[1153,5412,5413],{"class":1297},"      transport: ",[1153,5415,5326],{"class":4096},[1153,5417,5329],{"class":1605},[1153,5419,5318],{"class":1297},[1153,5421,5422,5425,5427,5429,5431,5433,5435,5437,5439],{"class":1155,"line":3288},[1153,5423,5424],{"class":1297},"        api: ",[1153,5426,5339],{"class":1609},[1153,5428,5342],{"class":1297},[1153,5430,33],{"class":1609},[1153,5432,5347],{"class":1297},[1153,5434,33],{"class":1609},[1153,5436,5352],{"class":1297},[1153,5438,5355],{"class":1609},[1153,5440,4246],{"class":1297},[1153,5442,5443],{"class":1155,"line":3293},[1153,5444,5445],{"class":1297},"      }),\n",[1153,5447,5448],{"class":1155,"line":3298},[1153,5449,4311],{"class":1297},[1153,5451,5452,5454],{"class":1155,"line":3303},[1153,5453,5135],{"class":4096},[1153,5455,5378],{"class":1297},[1153,5457,5458],{"class":1155,"line":3308},[1153,5459,3114],{"class":1297},[1153,5461,5462],{"class":1155,"line":4279},[1153,5463,2209],{"emptyLinePlaceholder":542},[1153,5465,5466,5469],{"class":1155,"line":4288},[1153,5467,5468],{"class":4096},"  return",[1153,5470,5471],{"class":1297}," { chat, ensureChat, newChat }\n",[1153,5473,5474],{"class":1155,"line":4302},[1153,5475,3120],{"class":1297},[14,5477,5478,5481],{},[256,5479,5480],{},"shallowRef"," dipakai agar Vue tidak deep-track seluruh internal state dari Chat object — cukup track referensi-nya saja.",[55,5483,5485],{"id":5484},"custom-sse-event-data-query-result","Custom SSE Event: data-query-result",[14,5487,5488,5489,1285],{},"Yang menarik adalah bagaimana SQL dan data table ditampilkan di UI. Saya mengextend protocol AI SDK v6 dengan custom event ",[256,5490,5491],{},"data-query-result",[1131,5493,5497],{"className":5494,"code":5495,"language":5496,"meta":982,"style":982},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\nconst queryResult = computed(() => {\n  const part = props.message.parts.find(p => p.type === 'data-query-result')\n  if (!part?.data) return undefined\n  return {\n    sql: part.data.sql,\n    data: part.data.data,\n    execution_time_ms: part.data.execution_time_ms,\n    total_rows: part.data.total_rows,\n  }\n})\n\u003C/script>\n","vue",[256,5498,5499,5519,5539,5574,5592,5598,5603,5608,5613,5618,5622,5627],{"__ignoreMap":982},[1153,5500,5501,5503,5506,5509,5512,5514,5517],{"class":1155,"line":1156},[1153,5502,1298],{"class":1297},[1153,5504,5505],{"class":1301},"script",[1153,5507,5508],{"class":1605}," setup",[1153,5510,5511],{"class":1605}," lang",[1153,5513,4125],{"class":1297},[1153,5515,5516],{"class":1609},"\"ts\"",[1153,5518,1305],{"class":1297},[1153,5520,5521,5523,5526,5528,5531,5534,5537],{"class":1155,"line":983},[1153,5522,5233],{"class":4096},[1153,5524,5525],{"class":1619}," queryResult",[1153,5527,4591],{"class":4096},[1153,5529,5530],{"class":1605}," computed",[1153,5532,5533],{"class":1297},"(() ",[1153,5535,5536],{"class":4096},"=>",[1153,5538,4752],{"class":1297},[1153,5540,5541,5544,5547,5549,5552,5555,5557,5560,5563,5566,5569,5572],{"class":1155,"line":988},[1153,5542,5543],{"class":4096},"  const",[1153,5545,5546],{"class":1619}," part",[1153,5548,4591],{"class":4096},[1153,5550,5551],{"class":1297}," props.message.parts.",[1153,5553,5554],{"class":1605},"find",[1153,5556,4404],{"class":1297},[1153,5558,14],{"class":5559},"s4XuR",[1153,5561,5562],{"class":4096}," =>",[1153,5564,5565],{"class":1297}," p.type ",[1153,5567,5568],{"class":4096},"===",[1153,5570,5571],{"class":1609}," 'data-query-result'",[1153,5573,5261],{"class":1297},[1153,5575,5576,5579,5581,5583,5586,5589],{"class":1155,"line":1172},[1153,5577,5578],{"class":4096},"  if",[1153,5580,5296],{"class":1297},[1153,5582,5299],{"class":4096},[1153,5584,5585],{"class":1297},"part?.data) ",[1153,5587,5588],{"class":4096},"return",[1153,5590,5591],{"class":1619}," undefined\n",[1153,5593,5594,5596],{"class":1155,"line":1178},[1153,5595,5468],{"class":4096},[1153,5597,4752],{"class":1297},[1153,5599,5600],{"class":1155,"line":1184},[1153,5601,5602],{"class":1297},"    sql: part.data.sql,\n",[1153,5604,5605],{"class":1155,"line":1190},[1153,5606,5607],{"class":1297},"    data: part.data.data,\n",[1153,5609,5610],{"class":1155,"line":1196},[1153,5611,5612],{"class":1297},"    execution_time_ms: part.data.execution_time_ms,\n",[1153,5614,5615],{"class":1155,"line":1202},[1153,5616,5617],{"class":1297},"    total_rows: part.data.total_rows,\n",[1153,5619,5620],{"class":1155,"line":1208},[1153,5621,3114],{"class":1297},[1153,5623,5624],{"class":1155,"line":3087},[1153,5625,5626],{"class":1297},"})\n",[1153,5628,5629,5631,5633],{"class":1155,"line":3093},[1153,5630,1346],{"class":1297},[1153,5632,5505],{"class":1301},[1153,5634,1305],{"class":1297},[14,5636,5637],{},"Event ini membawa metadata query (SQL yang di-generate, hasil data, execution time) yang terpisah dari text response. Di UI, hasilnya ditampilkan sebagai collapsible SQL viewer dan data table di bawah jawaban conversational.",[55,5639,5641],{"id":5640},"ui-components","UI Components",[14,5643,5644,5645,5648,5649,5652,5653,5656,5657,5660],{},"Frontend menggunakan ",[26,5646,5647],{},"shadcn-vue"," (New York style) + ",[26,5650,5651],{},"Tailwind CSS v4"," untuk komponen UI, ",[26,5654,5655],{},"@tanstack/vue-table"," untuk data table hasil query, dan ",[26,5658,5659],{},"@unovis/vue"," untuk chart di halaman dashboard.",[35,5662],{},[38,5664,5666],{"id":5665},"token-economy-menjaga-biaya-tetap-rendah","Token Economy: Menjaga Biaya Tetap Rendah",[14,5668,5669],{},"Beberapa keputusan untuk efisiensi token/biaya:",[145,5671,5672,5678,5684,5690],{},[66,5673,5674,5677],{},[26,5675,5676],{},"Schema cache (1 jam TTL)"," — Tidak perlu introspect database setiap request",[66,5679,5680,5683],{},[26,5681,5682],{},"Truncate data untuk LLM"," — LLM Call #2 hanya melihat 50 baris pertama, meskipun query mengembalikan ratusan baris. Cukup untuk generate insight yang akurat",[66,5685,5686,5689],{},[26,5687,5688],{},"System prompt yang ringkas"," — Few-shot examples minimal (5 contoh) tapi mencakup pattern utama",[66,5691,5692,5695],{},[26,5693,5694],{},"Auto-LIMIT 1000"," — Mencegah query yang mengembalikan jutaan baris",[35,5697],{},[38,5699,5701],{"id":5700},"hasil-dan-pembelajaran","Hasil dan Pembelajaran",[55,5703,5705],{"id":5704},"apa-yang-berhasil","Apa yang Berhasil",[63,5707,5708,5714,5720,5726],{},[66,5709,5710,5713],{},[26,5711,5712],{},"Akurasi SQL cukup tinggi"," untuk query umum — agregasi, filter tanggal, join, grouping",[66,5715,5716,5719],{},[26,5717,5718],{},"Streaming UX membuat app terasa cepat"," — meskipun total latency bisa 5-8 detik, user sudah melihat jawaban muncul setelah ~3 detik",[66,5721,5722,5725],{},[26,5723,5724],{},"Transparansi SQL membangun trust"," — user bisa lihat query yang di-generate dan verify hasilnya",[66,5727,5728,5731],{},[26,5729,5730],{},"Bilingual support"," bekerja natural — user bisa mix Bahasa Indonesia dan English",[55,5733,5735],{"id":5734},"apa-yang-bisa-diperbaiki","Apa yang Bisa Diperbaiki",[63,5737,5738,5744,5750,5756],{},[66,5739,5740,5743],{},[26,5741,5742],{},"Query kompleks"," (subquery bertingkat, window functions) kadang tidak akurat — perlu lebih banyak few-shot examples",[66,5745,5746,5749],{},[26,5747,5748],{},"Tidak ada conversation memory"," — setiap pertanyaan independen, tidak bisa \"follow-up\" dari pertanyaan sebelumnya",[66,5751,5752,5755],{},[26,5753,5754],{},"Rate limiting in-memory"," — reset saat app restart, perlu Redis untuk production",[66,5757,5758,5761],{},[26,5759,5760],{},"Hanya 2 tabel"," — perlu testing dengan schema yang lebih kompleks",[35,5763],{},[38,5765,5767],{"id":5766},"tech-stack-lengkap","Tech Stack Lengkap",[1427,5769,5770,5780],{},[1430,5771,5772],{},[1433,5773,5774,5777],{},[1436,5775,5776],{},"Layer",[1436,5778,5779],{},"Teknologi",[1443,5781,5782,5789,5796,5804,5811,5818,5826,5834,5842,5850,5858,5866],{},[1433,5783,5784,5786],{},[1448,5785,1941],{},[1448,5787,5788],{},"Nuxt 4 + Vue 3 (Composition API)",[1433,5790,5791,5793],{},[1448,5792,5641],{},[1448,5794,5795],{},"shadcn-vue + Tailwind CSS v4",[1433,5797,5798,5801],{},[1448,5799,5800],{},"Chat SDK",[1448,5802,5803],{},"@ai-sdk/vue v3 + Vercel AI SDK v6",[1433,5805,5806,5809],{},[1448,5807,5808],{},"Data Table",[1448,5810,5655],{},[1433,5812,5813,5816],{},[1448,5814,5815],{},"Charts",[1448,5817,5659],{},[1433,5819,5820,5823],{},[1448,5821,5822],{},"Backend",[1448,5824,5825],{},"FastAPI (Python 3.10+)",[1433,5827,5828,5831],{},[1448,5829,5830],{},"LLM",[1448,5832,5833],{},"Claude Sonnet (Anthropic API)",[1433,5835,5836,5839],{},[1448,5837,5838],{},"Database",[1448,5840,5841],{},"PostgreSQL 16",[1433,5843,5844,5847],{},[1448,5845,5846],{},"DB Client",[1448,5848,5849],{},"asyncpg (connection pool)",[1433,5851,5852,5855],{},[1448,5853,5854],{},"Containerization",[1448,5856,5857],{},"Docker + Docker Compose",[1433,5859,5860,5863],{},[1448,5861,5862],{},"CI/CD",[1448,5864,5865],{},"GitHub Actions → GHCR → VPS SSH deploy",[1433,5867,5868,5871],{},[1448,5869,5870],{},"Package Managers",[1448,5872,5873],{},"pnpm (frontend) + uv (backend)",[35,5875],{},[38,5877,2549],{"id":2548},[14,5879,5880,5881,5884,5885,5888],{},"POC ini membuktikan bahwa ",[26,5882,5883],{},"natural language to SQL sudah cukup mature"," untuk use case internal — terutama untuk query yang umum dan repetitif. Ini bukan menggantikan data engineer atau BI tools, tapi memberikan ",[1761,5886,5887],{},"fast lane"," bagi tim bisnis yang butuh jawaban cepat tanpa harus masuk antrian ticket.",[14,5890,5891],{},"Dual-LLM call pattern terbukti efektif untuk memisahkan concern antara SQL generation dan answer generation. Dan dengan security berlapis (validator + read-only user + audit log), risikonya bisa dikelola dengan baik.",[14,5893,5894,5895],{},"Kalau tertarik mencoba, demo-nya bisa diakses di: ",[26,5896,5897],{},[18,5898,3965],{"href":3963,"rel":5899},[22],[1302,5901,5902],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":982,"searchDepth":983,"depth":983,"links":5904},[5905,5906,5910,5916,5921,5926,5927,5931,5932],{"id":3970,"depth":983,"text":3971},{"id":4007,"depth":983,"text":4008,"children":5907},[5908,5909],{"id":4018,"depth":988,"text":4019},{"id":4064,"depth":988,"text":4065},{"id":4076,"depth":983,"text":4077,"children":5911},[5912,5913,5914,5915],{"id":4080,"depth":988,"text":4081},{"id":4384,"depth":988,"text":4385},{"id":4504,"depth":988,"text":4505},{"id":4733,"depth":988,"text":4734},{"id":4858,"depth":983,"text":4859,"children":5917},[5918,5919,5920],{"id":4865,"depth":988,"text":4866},{"id":5144,"depth":988,"text":5145},{"id":5182,"depth":988,"text":5183},{"id":5209,"depth":983,"text":5210,"children":5922},[5923,5924,5925],{"id":5213,"depth":988,"text":5214},{"id":5484,"depth":988,"text":5485},{"id":5640,"depth":988,"text":5641},{"id":5665,"depth":983,"text":5666},{"id":5700,"depth":983,"text":5701,"children":5928},[5929,5930],{"id":5704,"depth":988,"text":5705},{"id":5734,"depth":988,"text":5735},{"id":5766,"depth":983,"text":5767},{"id":2548,"depth":983,"text":2549},{"date":5934,"keywords":5935,"tags":5936,"slug":5940,"status":1027},"2026-03-17T08:00:00+07:00","AI, Natural Language to SQL, LLM, FastAPI, NuxtJS, Vue3, PostgreSQL",[1025,5937,5830,5938,5939],"Natural Language to SQL","FastAPI","NuxtJS","ai-chat-database-assistant","/blog/ai-chat-database-assistant",{"title":3927,"description":3945},"blog/ai-chat-database-assistant","81hL0FS5sedRKrZ3UQsHCtYoBzV1mcTL6wDlbjLWIA4",{"id":5946,"title":5947,"body":5948,"description":6566,"extension":1017,"meta":6567,"navigation":542,"ogImage":5955,"path":6574,"seo":6575,"stem":6576,"__hash__":6577},"blog/blog/vibe-coding-dengan-claude-code-saya-berhenti-menulis-code.md","Vibe Coding dengan Claude Code: Saya Berhenti Menulis Code",{"type":8,"value":5949,"toc":6557},[5950,5956,5959,5972,6010,6014,6043,6056,6082,6086,6115,6137,6144,6148,6176,6199,6226,6230,6269,6301,6326,6330,6358,6375,6414,6418,6424,6450,6493,6516,6518,6533,6536,6545,6552],[14,5951,5952],{},[278,5953],{"alt":5954,"src":5955},"Vibe Coding dengan Claude Code","./images/vibe-coding-dengan-claude-code-saya-berhenti-menulis-code.png",[1959,5957,5947],{"id":5958},"vibe-coding-dengan-claude-code-saya-berhenti-menulis-code",[11,5960,5961],{},[14,5962,5963,5964,5967,5968,5971],{},"Saya pikir ini cuma ",[1761,5965,5966],{},"hype",". Ternyata cara saya ",[1761,5969,5970],{},"ngoding"," benar-benar berubah.",[14,5973,5974,5975,5978,5979,5982,5983,5986,5987,5989,5990,5992,5993,5996,5997,6000,6001,2303,6004,2307,6007,33],{},"Beberapa bulan terakhir, istilah ",[1761,5976,5977],{},"vibe coding"," semakin sering terdengar. Sebuah pendekatan di mana ",[1761,5980,5981],{},"engineer"," lebih banyak berinteraksi dengan AI melalui ",[1761,5984,5985],{},"prompt",", sementara AI menjadi eksekutor utama penulisan ",[1761,5988,256],{},". Artikel ini bukan tentang ",[1761,5991,5966],{}," AI, bukan juga tutorial. Ini adalah refleksi jujur dari pengalaman saya menjalani ",[26,5994,5995],{},"full vibe coding"," di sebuah ",[1761,5998,5999],{},"project"," skala besar, dengan ",[1761,6002,6003],{},"real constraint",[1761,6005,6006],{},"real bug",[1761,6008,6009],{},"real pressure",[38,6011,6013],{"id":6012},"masuk-di-tengah-badai","Masuk di Tengah Badai",[14,6015,6016,6017,6020,6021,6023,6024,6027,6028,6031,6032,1767,6035,6038,6039,6042],{},"Ceritanya dimulai ketika saya di-",[1761,6018,6019],{},"assign"," ke sebuah ",[1761,6022,5999],{}," yang saya sebut Project X. ",[1761,6025,6026],{},"Monorepo"," besar: Next.js untuk ",[1761,6029,6030],{},"web user",", Next.js lagi untuk admin, dan Nest.js di ",[1761,6033,6034],{},"backend",[1761,6036,6037],{},"Developer"," sebelumnya sudah ",[1761,6040,6041],{},"resign",", dan saya masuk untuk melanjutkan.",[14,6044,6045,6046,6049,6050,6052,6053],{},"Saat itu, saya tidak punya ekspektasi apa-apa. Bahkan cenderung skeptis. Ini pengalaman pertama saya bekerja ",[1761,6047,6048],{},"full-time"," dengan Claude Code di ",[1761,6051,5999],{}," nyata. Yang ada di kepala saya cuma satu: ",[1761,6054,6055],{},"\"Ini beneran bisa jalan?\"",[14,6057,6058,6059,6062,6063,6066,6067,1850,6070,6073,6074,6077,6078,6081],{},"Hari-hari awal diisi kebingungan. ",[1761,6060,6061],{},"Codebase"," besar, ",[1761,6064,6065],{},"flow"," bisnis belum saya pahami, dan saya harus produktif secepat mungkin. Langkah pertama saya sederhana saja. ",[1761,6068,6069],{},"Setup project",[1761,6071,6072],{},"local",", tanya ",[1761,6075,6076],{},"developer"," yang masih ",[1761,6079,6080],{},"in charge",", dan mulai bertanya langsung ke Claude Code.",[38,6083,6085],{"id":6084},"kejutan-pertama-onboarding-dengan-ai","Kejutan Pertama: Onboarding dengan AI",[14,6087,6088,6089,6092,6093,6096,6097,6099,6100,6103,6104,6107,6108,6111,6112,33],{},"Yang tidak saya sangka, Claude Code ternyata sangat membantu proses ",[1761,6090,6091],{},"onboarding",". Saya bisa minta dia membaca ",[1761,6094,6095],{},"codebase",", menjelaskan ",[1761,6098,6065],{}," antar ",[1761,6101,6102],{},"module",", bahkan membantu ",[1761,6105,6106],{},"debugging error"," saat ",[1761,6109,6110],{},"setup",". Rasanya seperti punya senior yang sabar dan selalu ",[1761,6113,6114],{},"available",[14,6116,6117,6118,6121,6122,6125,6126,6129,6130,6133,6134,6136],{},"Pola kerja saya perlahan berubah tanpa saya sadari. Setiap kali ketemu ",[1761,6119,6120],{},"error",", alurnya jadi: baca ",[1761,6123,6124],{},"log",", kirim ke Claude Code, ",[1761,6127,6128],{},"review"," hasilnya, ",[1761,6131,6132],{},"test"," ulang. Bahkan untuk ",[1761,6135,6120],{}," kecil yang sebenarnya saya pahami, saya tetap minta Claude Code yang memperbaiki.",[14,6138,6139,6140,6143],{},"Di titik ini, saya mulai sadar satu hal: ",[26,6141,6142],{},"saya tidak lagi menjadi eksekutor utama."," Dan anehnya, itu bukan hal yang buruk.",[38,6145,6147],{"id":6146},"belajar-dari-kesalahan-prompting","Belajar dari Kesalahan Prompting",[14,6149,6150,6153,6154,6156,6157,6160,6161,6164,6165,6168,6169,6171,6172,6175],{},[1761,6151,6152],{},"Scope"," utama saya di ",[1761,6155,5999],{}," ini adalah ",[1761,6158,6159],{},"refactoring module"," tertentu berdasarkan ",[1761,6162,6163],{},"outstanding issue"," dari ",[1761,6166,6167],{},"client",". Awalnya saya mencoba pendekatan yang mungkin banyak orang lakukan. Satu ",[1761,6170,5985],{}," besar, langsung eksekusi, tanpa ",[1761,6173,6174],{},"Plan Mode",". Hasilnya? Sebagian benar, sebagian meleset.",[14,6177,6178,6179,6182,6183,6186,6187,1767,6189,6192,6193,6195,6196,33],{},"Dari situ saya belajar bahwa ",[1761,6180,6181],{},"task"," besar ",[26,6184,6185],{},"wajib"," pakai ",[1761,6188,6174],{},[1761,6190,6191],{},"Plan"," harus di-",[1761,6194,6128],{}," sebelum dieksekusi. Ini bukan opsional, ini ",[1761,6197,6198],{},"survival skill",[14,6200,6201,6202,1767,6205,6208,6209,2303,6212,6214,6215,6218,6219,6222,6223,33],{},"Keputusan penting lainnya: saya memilih untuk ",[26,6203,6204],{},"tidak menulis ulang dari nol",[1761,6206,6207],{},"Design"," Figma yang baru tidak terlalu ",[1761,6210,6211],{},"major",[1761,6213,256],{}," lama sudah berjalan, dan masalah utamanya lebih ke struktur dan ",[1761,6216,6217],{},"componentization",". Jadi pendekatan yang lebih masuk akal adalah ",[1761,6220,6221],{},"refactoring"," bertahap, bukan ",[1761,6224,6225],{},"rewrite",[38,6227,6229],{"id":6228},"yang-benar-benar-membantu-dan-yang-tidak","Yang Benar-Benar Membantu (dan Yang Tidak)",[14,6231,6232,6233,6236,6237,2303,6240,6242,6243,6246,6247,6250,6251,6253,6254,6257,6258,6264,6265,6268],{},"Kecepatan ",[1761,6234,6235],{},"development"," meningkat drastis, itu fakta. Untuk skenario seperti ",[1761,6238,6239],{},"join mid-project",[1761,6241,6221],{}," bertahap, dan ",[1761,6244,6245],{},"bug fixing"," dengan ",[1761,6248,6249],{},"repro"," yang jelas, ",[1761,6252,5977],{}," benar-benar ",[1761,6255,6256],{},"powerful",". Tapi kecepatan ini punya harga: ",[26,6259,6260,6263],{},[1761,6261,6262],{},"testing end-to-end"," harus sangat disiplin."," Karena kalau tidak, ",[1761,6266,6267],{},"bug"," dari AI bisa lolos tanpa terdeteksi.",[14,6270,6271,6272,6275,6276,1767,6279,6282,6283,6286,6287,6290,6291,6294,6295,1767,6297,6300],{},"Dan bicara soal friksi, ",[1761,6273,6274],{},"monorepo"," Next.js 15 + Nest.js itu sangat ",[1761,6277,6278],{},"memory hungry",[1761,6280,6281],{},"Compile time"," lama, ",[1761,6284,6285],{},"feedback loop"," melambat. Belum lagi biaya ",[1761,6288,6289],{},"subscription"," dan limitasi penggunaan model. Kualitas ",[1761,6292,6293],{},"output"," juga sangat bergantung pada kualitas ",[1761,6296,5985],{},[1761,6298,6299],{},"Garbage in, garbage out",", itu berlaku juga di sini.",[14,6302,6303,6304,6307,6308,6311,6312,6315,6316,6318,6319,6322,6323,6325],{},"Yang paling perlu diwaspadai: ",[26,6305,6306],{},"AI bisa salah dengan sangat percaya diri."," Claude Code cukup sering memberikan solusi yang terlihat benar, tapi salah secara ",[1761,6309,6310],{},"logic",". Solusi saya konsisten: ",[1761,6313,6314],{},"copy error"," atau hasil yang mencurigakan, minta Claude memperbaiki, ",[1761,6317,6128],{}," ulang. ",[1761,6320,6321],{},"Loop"," ini harus jadi kebiasaan. AI sangat cepat, tapi dia tidak bertanggung jawab atas dampak dari ",[1761,6324,256],{}," yang dia tulis. Itu tetap tanggung jawab kita.",[38,6327,6329],{"id":6328},"peran-baru-bukan-lagi-coder","Peran Baru: Bukan Lagi Coder",[14,6331,6332,6333,6335,6336,6341,6342,6351,6352,6354,6355,6357],{},"Di ",[1761,6334,5999],{}," ini, peran saya bergeser cukup signifikan. Dari ",[1761,6337,6338],{},[26,6339,6340],{},"coder"," menjadi ",[26,6343,6344,2303,6347,6350],{},[1761,6345,6346],{},"reviewer",[1761,6348,6349],{},"architect",", dan QA",". Produktivitas tidak lagi diukur dari seberapa banyak ",[1761,6353,256],{}," yang ditulis, tapi dari kualitas arahan, ketajaman ",[1761,6356,6128],{},", dan kecepatan validasi hasil.",[14,6359,6360,6361,1755,6364,1767,6367,6370,6371,6374],{},"Ini juga yang membuat saya sadar bahwa ",[1761,6362,6363],{},"prompting",[1761,6365,6366],{},"no-skill activity",[1761,6368,6369],{},"Task"," besar butuh konteks yang eksplisit, kadang perlu menyertakan ",[1761,6372,6373],{},"link"," dokumentasi, dan pemilihan model juga berpengaruh. Opus 4.5 lebih akurat tapi mahal, Sonnet 4.5 lebih cepat dan ringan.",[14,6376,6377,6378,6381,6382,214,6389,6392,6393,6396,6397,6400,6401,6404,6405,6407,6408,6410,6411,33],{},"Lalu ada satu ",[1761,6379,6380],{},"insight"," yang mungkin tidak banyak dibahas orang: ",[26,6383,6384,6385,6388],{},"literasi menjadi ",[1761,6386,6387],{},"bottleneck"," baru.",[1761,6390,6391],{},"Prompt"," itu menulis, ",[1761,6394,6395],{},"review output"," AI itu membaca. ",[1761,6398,6399],{},"Engineer"," yang cepat membaca dan menulis akan mendapat ",[1761,6402,6403],{},"leverage"," AI yang jauh lebih besar. Dan menariknya, bahasa ",[1761,6406,5985],{}," tidak menjadi masalah. Mayoritas ",[1761,6409,5985],{}," saya pakai bahasa Indonesia, dan Claude Code tetap memahami konteks dengan baik. Yang penting bukan bahasanya, tapi kejelasan konteks dan ",[1761,6412,6413],{},"intent",[38,6415,6417],{"id":6416},"dampak-ke-karier-engineer","Dampak ke Karier Engineer",[14,6419,6420,6421,6423],{},"Peran ",[1761,6422,5981],{}," berubah, bukan hilang. Dan dampaknya berbeda di setiap level.",[14,6425,6426,6431,6432,6435,6436,6438,6439,6442,6443,6445,6446,6449],{},[1761,6427,6428],{},[26,6429,6430],{},"Junior engineer"," sekarang punya pilihan. Belajar ",[1761,6433,6434],{},"fundamentals"," dulu baru AI, atau langsung terbiasa dengan AI. Keduanya valid, tapi junior yang melewatkan ",[1761,6437,6434],{}," akan kesulitan saat ",[1761,6440,6441],{},"debugging edge case"," yang AI tidak mengerti, atau saat harus menentukan apakah ",[1761,6444,6293],{}," AI benar atau hanya ",[1761,6447,6448],{},"terlihat"," meyakinkan.",[14,6451,6452,6457,6458,2303,6461,2303,6464,2303,6467,6470,6471,6474,6475,6478,6479,2334,6482,6485,6486,6489,6490,33],{},[1761,6453,6454],{},[26,6455,6456],{},"Senior engineer"," justru semakin bernilai. ",[1761,6459,6460],{},"Architecture",[1761,6462,6463],{},"system design",[1761,6465,6466],{},"decision making under uncertainty",[1761,6468,6469],{},"quality control",". Ini semua area yang tidak bisa di-",[1761,6472,6473],{},"outsource"," ke AI. ",[1761,6476,6477],{},"Skill"," seperti ",[1761,6480,6481],{},"problem decomposition",[1761,6483,6484],{},"critical thinking"," jadi lebih ",[1761,6487,6488],{},"valuable"," daripada ",[1761,6491,6492],{},"syntax recall",[14,6494,6495,6500,6501,6503,6504,6506,6507,6509,6510,6512,6513,33],{},[1761,6496,6497],{},[26,6498,6499],{},"Mid-level engineer"," berada di posisi paling menarik. Sudah punya cukup ",[1761,6502,6434],{}," untuk ",[1761,6505,6128],{}," AI ",[1761,6508,6293],{},", dan bisa ",[1761,6511,6403],{}," AI untuk akselerasi transisi ke ",[1761,6514,6515],{},"senior role",[38,6517,2549],{"id":2548},[14,6519,6520,6523,6524,6526,6527,6529,6530,33],{},[1761,6521,6522],{},"Vibe coding"," dengan Claude Code membuat kerja jauh lebih cepat. Tapi saya tidak berhenti menjadi ",[1761,6525,5981],{},". Saya hanya memindahkan energi dari menulis ",[1761,6528,256],{}," ke ",[26,6531,6532],{},"menjaga kualitas keputusan",[14,6534,6535],{},"Jika ada satu kalimat yang paling menggambarkan pengalaman ini:",[11,6537,6538],{},[14,6539,6540,6541,6544],{},"Perlakukan AI seperti ",[1761,6542,6543],{},"junior engineer"," yang sangat cepat dan pintar, tapi tidak bisa membaca pikiran kita.",[14,6546,6547,6548,6551],{},"Semakin jelas konteks yang kita berikan, semakin baik hasil yang kita dapatkan. Tapi lebih penting dari itu: semakin baik kita memahami ",[1761,6549,6550],{},"problem",", semakin efektif kita bisa memberikan konteks tersebut.",[14,6553,6554],{},[26,6555,6556],{},"Konteks adalah segalanya. Dan pemahaman adalah prasyarat untuk konteks yang baik.",{"title":982,"searchDepth":983,"depth":983,"links":6558},[6559,6560,6561,6562,6563,6564,6565],{"id":6012,"depth":983,"text":6013},{"id":6084,"depth":983,"text":6085},{"id":6146,"depth":983,"text":6147},{"id":6228,"depth":983,"text":6229},{"id":6328,"depth":983,"text":6329},{"id":6416,"depth":983,"text":6417},{"id":2548,"depth":983,"text":2549},"Saya pikir ini cuma hype. Ternyata cara saya ngoding benar-benar berubah.",{"date":6568,"keywords":6569,"tags":6570,"slug":5958,"status":1027},"2026-02-02T08:00:00+07:00","Vibe Coding, Claude Code, Project Skala Besar, Pelajaran Nyata",[6571,583,6572,6573],"Vibe Coding","Project Skala Besar","Pelajaran Nyata","/blog/vibe-coding-dengan-claude-code-saya-berhenti-menulis-code",{"title":5947,"description":6566},"blog/vibe-coding-dengan-claude-code-saya-berhenti-menulis-code","VlPQjRNAL0clkZP01z1jWBm-7nroHr_-Q-OO_6CD6Qg",{"id":6579,"title":6580,"body":6581,"description":6839,"extension":1017,"meta":6840,"navigation":542,"ogImage":6587,"path":6850,"seo":6851,"stem":6852,"__hash__":6853},"blog/blog/nuxt-v4.md","Nuxt 4.0 Resmi Dirilis: Apa yang Baru dan Bagaimana Cara Migrasi?",{"type":8,"value":6582,"toc":6828},[6583,6591,6595,6602,6604,6612,6643,6645,6649,6691,6693,6697,6712,6714,6718,6733,6735,6739,6746,6764,6767,6774,6789,6792,6800,6802,6806,6817,6819,6822,6825],[14,6584,6585],{},[278,6586],{"src":6587,"alt":6588,"className":6589,"style":6590},"https://nuxt.com/assets/blog/v4.png","Nuxia",[539],"view-transition-name: nuxt-v4;",[38,6592,6594],{"id":6593},"nuxt-40-resmi-dirilis","🚀 Nuxt 4.0 Resmi Dirilis",[14,6596,6597,6598,6601],{},"Nuxt 4.0 dirilis resmi pada 15 Juli 2025 setelah lebih dari setahun dalam fase uji kompatibilitas. Ini adalah major release yang menitikberatkan pada stabilitas dan penginkatan ",[1761,6599,6600],{},"developer experience"," -meski ada beberapa perubahan yang bersifat breaking, tim Nuxt berusaha menjaga jalur migrasi sehalus mungkin.",[35,6603],{},[38,6605,6607,6608,6611],{"id":6606},"_1-struktur-proyek-baru-app-directory","📂 1. Struktur Proyek Baru: ",[256,6609,6610],{},"app/"," Directory",[63,6613,6614,6631,6640],{},[66,6615,6616,6617,6619,6620,2334,6623,6626,6627,6630],{},"Aplikasi kini ditempatkan dalam folder ",[256,6618,6610],{},", memisahkan kode dari ",[256,6621,6622],{},"node_modules/",[256,6624,6625],{},".git/",", sehingga mempercepat ",[26,6628,6629],{},"file watchers"," (terutama pada Windows & Linux).",[66,6632,6633,6634],{},"Struktur defaultnya:",[1131,6635,6638],{"className":6636,"code":6637,"language":1136},[1134],"my-nuxt-app/\n├─ app/\n│  ├─ assets/\n│  ├─ components/\n│  ├─ pages/\n│  ├─ layouts/\n│  ├─ server/\n│  └─ ...\n├─ shared/\n├─ public/\n├─ server/\n└─ nuxt.config.ts\n",[256,6639,6637],{"__ignoreMap":982},[66,6641,6642],{},"IDE kini mampu membedakan antara kode client dan server, meningkatkan produktivitas. Meski demikian, struktur lama masih didukung—Nuxt akan tetap mendeteksi dan mendukungnya.",[35,6644],{},[38,6646,6648],{"id":6647},"️-2-data-fetching-yang-lebih-pintar","⚙️ 2. Data Fetching yang \"Lebih Pintar\"",[63,6650,6651,6668,6671,6678],{},[66,6652,6653,2334,6656,6659,6660,6663,6664,6667],{},[256,6654,6655],{},"useAsyncData",[256,6657,6658],{},"useFetch"," kini otomatis ",[26,6661,6662],{},"berbagi data"," saat menggunakan ",[256,6665,6666],{},"key"," yang sama antara beberapa komponen.",[66,6669,6670],{},"Cleanup otomatis saat komponen unmount.",[66,6672,6673,6674,6677],{},"Support ",[26,6675,6676],{},"reactive keys"," untuk refetch dinamis.",[66,6679,6680,6681,6684,6685,2014,6688,33],{},"Kontrol cache lebih fleksibel—mode ",[256,6682,6683],{},"dedupe"," kini menerima opsi ",[256,6686,6687],{},"cancel",[256,6689,6690],{},"defer",[35,6692],{},[38,6694,6696],{"id":6695},"_3-perbaikan-dukungan-typescript","🧠 3. Perbaikan Dukungan TypeScript",[63,6698,6699,6702,6705],{},[66,6700,6701],{},"Nuxt 4 membagi proyek TypeScript ke dalam beberapa konteks: aplikasi, server, shared, dan builder.",[66,6703,6704],{},"Hasilnya: autocompletion lebih akurat, inference lebih baik, dan error yang dihasilkan lebih relevan.",[66,6706,6707,6708,6711],{},"Cukup satu ",[256,6709,6710],{},"tsconfig.json"," di root—lebih sederhana!.",[35,6713],{},[38,6715,6717],{"id":6716},"_4-cli-dev-experience-yang-lebih-cepat","⚡ 4. CLI & Dev Experience yang Lebih Cepat",[63,6719,6720,6723,6730],{},[66,6721,6722],{},"Waktu cold starts lebih singkat, berkat cache kompilasi V8.",[66,6724,6725,6726,6729],{},"File watcher native menggunakan ",[256,6727,6728],{},"fs.watch",", menghemat resource.",[66,6731,6732],{},"Komunikasi antara CLI dan Vite dev server kini via socket internal—mengurangi overhead terutama di Windows.",[35,6734],{},[38,6736,6738],{"id":6737},"️-5-migrasi-ke-nuxt4","🛠️ 5. Migrasi ke Nuxt 4",[145,6740,6741],{},[66,6742,6743],{},[26,6744,6745],{},"Upgrade Nuxt",[1131,6747,6749],{"className":1596,"code":6748,"language":1598,"meta":982,"style":982},"npx nuxt upgrade --dedupe\n",[256,6750,6751],{"__ignoreMap":982},[1153,6752,6753,6755,6758,6761],{"class":1155,"line":1156},[1153,6754,1606],{"class":1605},[1153,6756,6757],{"class":1609}," nuxt",[1153,6759,6760],{"class":1609}," upgrade",[1153,6762,6763],{"class":1619}," --dedupe\n",[14,6765,6766],{},"Ini memastikan lockfile di-dedup dan dependency Nuxt lainnya ikut diperbarui.",[145,6768,6769],{"start":983},[66,6770,6771],{},[26,6772,6773],{},"Migrasi dengan Codemod (opsional)",[1131,6775,6777],{"className":1596,"code":6776,"language":1598,"meta":982,"style":982},"npx codemod@latest nuxt/4/migration-recipe\n",[256,6778,6779],{"__ignoreMap":982},[1153,6780,6781,6783,6786],{"class":1155,"line":1156},[1153,6782,1606],{"class":1605},[1153,6784,6785],{"class":1609}," codemod@latest",[1153,6787,6788],{"class":1609}," nuxt/4/migration-recipe\n",[14,6790,6791],{},"(Tersedia juga untuk yarn/pnpm/bun).",[145,6793,6794],{"start":988},[66,6795,6796,6799],{},[26,6797,6798],{},"Uji & Sesuaikan","\nJalankan testing, kompilasi, dan cek tipe atau potensi konflik di modul. Upgrade guide mencakup banyak detail & opsi backward compatibility.",[35,6801],{},[38,6803,6805],{"id":6804},"_6-apa-agenda-selanjutnya","🎯 6. Apa Agenda Selanjutnya?",[63,6807,6808,6811,6814],{},[66,6809,6810],{},"Nuxt 4 dapatkan patch cepat untuk fix bug.",[66,6812,6813],{},"Nuxt 3 akan tetap ter-maintain hingga Januari 2026, memberi cukup waktu buat migrasi.",[66,6815,6816],{},"Nuxt 5 (yang akan memasukkan Nitro v3 dan h3 v2) diperkirakan rilis beberapa bulan setelah Nuxt 4.",[35,6818],{},[38,6820,6821],{"id":886},"✅ Kesimpulan",[14,6823,6824],{},"Nuxt 4 adalah upgrade berfokus pada developer experience: struktur proyek lebih rapi, tipe lebih akurat, data fetching lebih pintar, dan dev environment lebih cepat. Dengan tooling migrasi yang lengkap dan fase stabilitas yang matang, kamu bisa mulai adaptasi sekarang—atau migrasi segera setelah siap.",[1302,6826,6827],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":982,"searchDepth":983,"depth":983,"links":6829},[6830,6831,6833,6834,6835,6836,6837,6838],{"id":6593,"depth":983,"text":6594},{"id":6606,"depth":983,"text":6832},"📂 1. Struktur Proyek Baru: app/ Directory",{"id":6647,"depth":983,"text":6648},{"id":6695,"depth":983,"text":6696},{"id":6716,"depth":983,"text":6717},{"id":6737,"depth":983,"text":6738},{"id":6804,"depth":983,"text":6805},{"id":886,"depth":983,"text":6821},"Nuxt 4.0 adalah pembaruan besar untuk framework Nuxt.js yang membawa banyak fitur baru dan perbaikan.",{"date":6841,"keywords":6842,"tags":6843},"2025-07-21","Nuxt 4, Nuxt.js, TypeScript, data fetching, CLI, dev experience, migrasi Nuxt 4",[6844,6845,6846,6847,6848,6849],"Nuxt 4","Nuxt.js","TypeScript","data fetching","CLI","dev experience","/blog/nuxt-v4",{"title":6580,"description":6839},"blog/nuxt-v4","JWNwyqn-kNGcM7DI_F5KudoJudy0rqofKsii5RLINHk",{"id":6855,"title":6856,"body":6857,"description":7199,"extension":1017,"meta":7200,"navigation":542,"ogImage":6863,"path":7205,"seo":7206,"stem":7207,"__hash__":7208},"blog/blog/memahami-konsep-data-binding-dengan-cara-yang-simpel.md","Memahami Konsep Data Binding di Frontend (Dengan Cara yang Simpel!)",{"type":8,"value":6858,"toc":7189},[6859,6866,6869,6880,6884,6898,6902,6905,6916,6919,6937,6940,6946,6957,6963,6969,6971,6975,6978,6982,6989,6992,7010,7019,7023,7030,7033,7068,7082,7084,7088,7091,7160,7162,7164,7171,7183,7186],[14,6860,6861],{},[278,6862],{"src":6863,"alt":6588,"className":6864,"style":6865},"https://images.unsplash.com/photo-1750966580622-08d4d0812d57?q=80&w=2064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",[539],"view-transition-name: memahami-konsep-data-binding-dengan-cara-yang-simpel;",[1959,6867,6856],{"id":6868},"memahami-konsep-data-binding-di-frontend-dengan-cara-yang-simpel",[14,6870,6871,6872,6875,6876,6879],{},"Dalam pengembangan frontend, ",[26,6873,6874],{},"data binding"," adalah salah satu konsep penting yang wajib dipahami, apalagi jika kamu sedang belajar framework seperti Vue, React, atau Angular. Tapi tenang, di artikel ini kita akan bahas ",[26,6877,6878],{},"dengan bahasa yang sederhana",", tanpa istilah yang bikin mumet.",[38,6881,6883],{"id":6882},"apa-itu-data-binding","Apa Itu Data Binding?",[14,6885,6886,6887,6890,6891,6894,6895,33],{},"Secara sederhana, ",[26,6888,6889],{},"data binding adalah cara menghubungkan data dengan tampilan (UI)",". Jadi, ketika data berubah, tampilan ikut berubah ",[26,6892,6893],{},"secara otomatis",". Bahkan, dalam beberapa kasus, saat tampilan berubah (misalnya saat user mengetik), data juga ikut berubah. Itulah yang disebut ",[26,6896,6897],{},"two-way binding",[38,6899,6901],{"id":6900},"ilustrasi-simpel","Ilustrasi Simpel",[14,6903,6904],{},"Misalnya kamu punya data:",[1131,6906,6910],{"className":6907,"code":6908,"language":6909,"meta":982,"style":982},"language-js shiki shiki-themes github-light github-dark","const nama = 'Warsono'\n","js",[256,6911,6912],{"__ignoreMap":982},[1153,6913,6914],{"class":1155,"line":1156},[1153,6915,6908],{},[14,6917,6918],{},"Dan di HTML kamu punya:",[1131,6920,6922],{"className":1288,"code":6921,"language":1290,"meta":982,"style":982},"\u003Cp>Halo, {{ nama }}\u003C/p>\n",[256,6923,6924],{"__ignoreMap":982},[1153,6925,6926,6928,6930,6933,6935],{"class":1155,"line":1156},[1153,6927,1298],{"class":1297},[1153,6929,14],{"class":1301},[1153,6931,6932],{"class":1297},">Halo, {{ nama }}\u003C/",[1153,6934,14],{"class":1301},[1153,6936,1305],{"class":1297},[14,6938,6939],{},"Maka yang akan tampil di halaman adalah:",[1131,6941,6944],{"className":6942,"code":6943,"language":1136,"meta":982},[1134],"Halo, Warsono\n",[256,6945,6943],{"__ignoreMap":982},[14,6947,6948,6949,6952,6953,6956],{},"Nah, kalau ",[256,6950,6951],{},"nama"," berubah menjadi ",[256,6954,6955],{},"\"Gravitano\"",", maka tampilan akan langsung update jadi:",[1131,6958,6961],{"className":6959,"code":6960,"language":1136,"meta":982},[1134],"Halo, Gravitano\n",[256,6962,6960],{"__ignoreMap":982},[14,6964,6965,6966,33],{},"Inilah keajaiban data binding — ",[26,6967,6968],{},"data dan UI terhubung langsung",[35,6970],{},[38,6972,6974],{"id":6973},"jenis-jenis-data-binding","Jenis-jenis Data Binding",[14,6976,6977],{},"Ada dua jenis utama yang sering digunakan:",[55,6979,6981],{"id":6980},"_1-one-way-binding-satu-arah","1. One-Way Binding (Satu Arah)",[14,6983,6984,6985,6988],{},"Data hanya mengalir dari ",[26,6986,6987],{},"data → ke tampilan",". Cocok untuk kasus ketika kamu hanya ingin menampilkan data ke user.",[14,6990,6991],{},"Contoh (misalnya di Vue atau React):",[1131,6993,6995],{"className":1288,"code":6994,"language":1290,"meta":982,"style":982},"\u003Cp>{{ nama }}\u003C/p>\n",[256,6996,6997],{"__ignoreMap":982},[1153,6998,6999,7001,7003,7006,7008],{"class":1155,"line":1156},[1153,7000,1298],{"class":1297},[1153,7002,14],{"class":1301},[1153,7004,7005],{"class":1297},">{{ nama }}\u003C/",[1153,7007,14],{"class":1301},[1153,7009,1305],{"class":1297},[14,7011,7012,7013,7015,7016,7018],{},"Kalau kamu ubah nilai ",[256,7014,6951],{},", maka UI akan berubah. Tapi kalau user mengubah UI (misalnya mengetik sesuatu), nilai ",[256,7017,6951],{}," tidak akan ikut berubah.",[55,7020,7022],{"id":7021},"_2-two-way-binding-dua-arah","2. Two-Way Binding (Dua Arah)",[14,7024,7025,7026,7029],{},"Data dan tampilan ",[26,7027,7028],{},"saling terhubung",". Ketika user mengubah nilai di UI (misalnya mengetik di input), nilai di data juga ikut berubah.",[14,7031,7032],{},"Contoh di Vue:",[1131,7034,7036],{"className":1288,"code":7035,"language":1290,"meta":982,"style":982},"\u003Cinput v-model=\"nama\" />\n\u003Cp>Halo, {{ nama }}\u003C/p>\n",[256,7037,7038,7056],{"__ignoreMap":982},[1153,7039,7040,7042,7045,7048,7050,7053],{"class":1155,"line":1156},[1153,7041,1298],{"class":1297},[1153,7043,7044],{"class":1301},"input",[1153,7046,7047],{"class":1605}," v-model",[1153,7049,4125],{"class":1297},[1153,7051,7052],{"class":1609},"\"nama\"",[1153,7054,7055],{"class":1297}," />\n",[1153,7057,7058,7060,7062,7064,7066],{"class":1155,"line":983},[1153,7059,1298],{"class":1297},[1153,7061,14],{"class":1301},[1153,7063,6932],{"class":1297},[1153,7065,14],{"class":1301},[1153,7067,1305],{"class":1297},[63,7069,7070,7076],{},[66,7071,7072,7073,7075],{},"Ketika user mengetik di input, ",[256,7074,6951],{}," berubah.",[66,7077,7078,7079,7081],{},"Karena ",[256,7080,6951],{}," berubah, teks di paragraf juga ikut update otomatis.",[35,7083],{},[38,7085,7087],{"id":7086},"data-binding-di-framework-populer","Data Binding di Framework Populer",[14,7089,7090],{},"Berikut beberapa contoh data binding di framework terkenal:",[1427,7092,7093,7106],{},[1430,7094,7095],{},[1433,7096,7097,7100,7103],{},[1436,7098,7099],{},"Framework",[1436,7101,7102],{},"One-way",[1436,7104,7105],{},"Two-way",[1443,7107,7108,7127,7144],{},[1433,7109,7110,7115,7121],{},[1448,7111,7112],{},[26,7113,7114],{},"React",[1448,7116,7117,7118],{},"✅ ",[256,7119,7120],{},"{nama}",[1448,7122,7123,7124,2782],{},"❌ (manual pakai ",[256,7125,7126],{},"useState",[1433,7128,7129,7134,7139],{},[1448,7130,7131],{},[26,7132,7133],{},"Vue",[1448,7135,7117,7136],{},[256,7137,7138],{},"{{ nama }}",[1448,7140,7117,7141],{},[256,7142,7143],{},"v-model",[1433,7145,7146,7151,7155],{},[1448,7147,7148],{},[26,7149,7150],{},"Angular",[1448,7152,7117,7153],{},[256,7154,7138],{},[1448,7156,7117,7157],{},[256,7158,7159],{},"[(ngModel)]",[35,7161],{},[38,7163,887],{"id":886},[11,7165,7166],{},[14,7167,7168],{},[26,7169,7170],{},"Data binding = menghubungkan data dan tampilan, supaya sinkron otomatis.",[14,7172,7173,7174,2303,7177,2307,7180,33],{},"Dengan memahami data binding, kamu bisa membangun UI yang ",[26,7175,7176],{},"interaktif",[26,7178,7179],{},"dinamis",[26,7181,7182],{},"lebih efisien",[14,7184,7185],{},"Semakin dalam kamu belajar, semakin kamu akan sadar bahwa data binding adalah fondasi dari banyak fitur keren di frontend!",[1302,7187,7188],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":982,"searchDepth":983,"depth":983,"links":7190},[7191,7192,7193,7197,7198],{"id":6882,"depth":983,"text":6883},{"id":6900,"depth":983,"text":6901},{"id":6973,"depth":983,"text":6974,"children":7194},[7195,7196],{"id":6980,"depth":988,"text":6981},{"id":7021,"depth":988,"text":7022},{"id":7086,"depth":983,"text":7087},{"id":886,"depth":983,"text":887},"Data binding adalah cara menghubungkan data dengan tampilan (UI) di frontend. Dalam artikel ini, kita akan membahas konsep ini dengan bahasa yang sederhana dan mudah dipahami.",{"date":7201,"keywords":7202,"tags":7203},"2025-07-10","Data Binding, Frontend, Vue, React, Angular, One-way Binding, Two-way Binding",[7204,1941,7133,7114,7150],"Data Binding","/blog/memahami-konsep-data-binding-dengan-cara-yang-simpel",{"title":6856,"description":7199},"blog/memahami-konsep-data-binding-dengan-cara-yang-simpel","kuJFayFLJ-bFlqtEurEjBnTwZWHHEInD6eOBbjKcEYc",{"id":7210,"title":7211,"body":7212,"description":8541,"extension":1017,"meta":8542,"navigation":542,"ogImage":7218,"path":8549,"seo":8550,"stem":8551,"__hash__":8552},"blog/blog/mengenal-motion-vue-animasi-modern-dan-ringan-untuk-vuejs.md","Mengenal Motion Vue: Animasi Modern & Ringan untuk Vue.js",{"type":8,"value":7213,"toc":8516},[7214,7222,7225,7232,7238,7245,7247,7251,7260,7263,7267,7280,7284,7287,7291,7307,7311,7314,7316,7320,7332,7474,7480,7482,7489,7631,7636,7638,7645,8021,8026,8028,8035,8184,8189,8191,8203,8292,8297,8299,8306,8446,8451,8453,8457,8460,8488,8491,8493,8496,8499,8513],[14,7215,7216],{},[278,7217],{"src":7218,"alt":7219,"className":7220,"style":7221},"https://images.unsplash.com/photo-1534078362425-387ae9668c17?q=80&w=3538&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D","Motion Vue",[539],"view-transition-name: blog-post-image-mengenal-motion-vue-animasi-modern-dan-ringan-untuk-vuejs;",[1959,7223,7211],{"id":7224},"mengenal-motion-vue-animasi-modern-ringan-untuk-vuejs",[14,7226,7227,7228,7231],{},"Ingin bikin UI Vue-mu jadi lebih hidup dan interaktif? Tapi males ribet pakai CSS ",[256,7229,7230],{},"@keyframes"," atau library berat seperti GSAP?",[14,7233,7234,7235,7237],{},"Kenalin: ",[26,7236,7219],{}," — cara baru dan menyenangkan untuk membangun antarmuka dengan animasi yang mulus, ringan, dan sepenuhnya terintegrasi dengan Vue 3.",[11,7239,7240],{},[14,7241,7242],{},[26,7243,7244],{},"\"Build beautiful motion-driven interfaces with Vue.js based on motion. Simple, powerful, and performant animations for modern web applications.\"",[35,7246],{},[38,7248,7250],{"id":7249},"kenapa-harus-pakai-motion-vue","Kenapa Harus Pakai Motion Vue?",[14,7252,7253,7254,7259],{},"Motion Vue dibangun di atas ",[18,7255,7258],{"href":7256,"rel":7257},"https://motion.dev/",[22],"Motion One",", animasi engine berbasis Web Animations API dari tim yang sama di balik Framer Motion.",[14,7261,7262],{},"Berikut alasan kenapa kamu mungkin langsung jatuh hati:",[55,7264,7266],{"id":7265},"simpel-dan-deklaratif","✅ Simpel dan Deklaratif",[14,7268,7269,7270,7273,7274,2334,7277,33],{},"Tidak perlu mikirin event lifecycle atau ",[256,7271,7272],{},"watch"," — cukup ",[256,7275,7276],{},":animate",[256,7278,7279],{},":transition",[55,7281,7283],{"id":7282},"super-ringan","⚡ Super Ringan",[14,7285,7286],{},"Dibanding library lain, Motion Vue minim dependensi dan sangat cepat. Cocok untuk app skala kecil sampai besar.",[55,7288,7290],{"id":7289},"terintegrasi-reaktif","🧠 Terintegrasi Reaktif",[14,7292,7293,7294,2303,7297,2303,7300,2307,7303,7306],{},"Props seperti ",[256,7295,7296],{},"initial",[256,7298,7299],{},"hover",[256,7301,7302],{},"press",[256,7304,7305],{},"in-view"," semuanya reactive. Cocok banget buat Vue dev.",[55,7308,7310],{"id":7309},"kombinasi-sempurna-dengan-tailwind","🎨 Kombinasi Sempurna dengan Tailwind",[14,7312,7313],{},"Layout pakai Tailwind, animasi pakai Motion Vue — hasilnya? UI bersih dan ciamik!",[35,7315],{},[38,7317,7319],{"id":7318},"contoh-contoh-penggunaan-motion-vue","💡 Contoh-Contoh Penggunaan Motion Vue",[55,7321,7323,7324],{"id":7322},"_1-animasi-dasar-dengan-initial-dan-animate","1. ",[26,7325,7326,7327,2334,7329],{},"Animasi Dasar dengan ",[256,7328,7296],{},[256,7330,7331],{},"animate",[1131,7333,7335],{"className":5494,"code":7334,"language":5496,"meta":982,"style":982},"\u003Cscript setup lang=\"ts\">\nimport { Motion } from 'motion-v'\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CMotion\n    class=\"bg-primary w-1/3 aspect-square rounded-2xl\"\n    :initial=\"{ scale: 0 }\"\n    :animate=\"{ rotate: 180, scale: 1 }\"\n    :transition=\"{\n      type: 'spring',\n      stiffness: 260,\n      damping: 20,\n      delay: 0.3,\n    }\"\n  />\n\u003C/template>\n",[256,7336,7337,7353,7367,7375,7379,7388,7396,7406,7416,7426,7436,7441,7446,7451,7456,7461,7466],{"__ignoreMap":982},[1153,7338,7339,7341,7343,7345,7347,7349,7351],{"class":1155,"line":1156},[1153,7340,1298],{"class":1297},[1153,7342,5505],{"class":1301},[1153,7344,5508],{"class":1605},[1153,7346,5511],{"class":1605},[1153,7348,4125],{"class":1297},[1153,7350,5516],{"class":1609},[1153,7352,1305],{"class":1297},[1153,7354,7355,7358,7361,7364],{"class":1155,"line":983},[1153,7356,7357],{"class":4096},"import",[1153,7359,7360],{"class":1297}," { Motion } ",[1153,7362,7363],{"class":4096},"from",[1153,7365,7366],{"class":1609}," 'motion-v'\n",[1153,7368,7369,7371,7373],{"class":1155,"line":988},[1153,7370,1346],{"class":1297},[1153,7372,5505],{"class":1301},[1153,7374,1305],{"class":1297},[1153,7376,7377],{"class":1155,"line":1172},[1153,7378,2209],{"emptyLinePlaceholder":542},[1153,7380,7381,7383,7386],{"class":1155,"line":1178},[1153,7382,1298],{"class":1297},[1153,7384,7385],{"class":1301},"template",[1153,7387,1305],{"class":1297},[1153,7389,7390,7393],{"class":1155,"line":1184},[1153,7391,7392],{"class":1297},"  \u003C",[1153,7394,7395],{"class":1301},"Motion\n",[1153,7397,7398,7401,7403],{"class":1155,"line":1190},[1153,7399,7400],{"class":1605},"    class",[1153,7402,4125],{"class":1297},[1153,7404,7405],{"class":1609},"\"bg-primary w-1/3 aspect-square rounded-2xl\"\n",[1153,7407,7408,7411,7413],{"class":1155,"line":1196},[1153,7409,7410],{"class":1605},"    :initial",[1153,7412,4125],{"class":1297},[1153,7414,7415],{"class":1609},"\"{ scale: 0 }\"\n",[1153,7417,7418,7421,7423],{"class":1155,"line":1202},[1153,7419,7420],{"class":1605},"    :animate",[1153,7422,4125],{"class":1297},[1153,7424,7425],{"class":1609},"\"{ rotate: 180, scale: 1 }\"\n",[1153,7427,7428,7431,7433],{"class":1155,"line":1208},[1153,7429,7430],{"class":1605},"    :transition",[1153,7432,4125],{"class":1297},[1153,7434,7435],{"class":1609},"\"{\n",[1153,7437,7438],{"class":1155,"line":3087},[1153,7439,7440],{"class":1609},"      type: 'spring',\n",[1153,7442,7443],{"class":1155,"line":3093},[1153,7444,7445],{"class":1609},"      stiffness: 260,\n",[1153,7447,7448],{"class":1155,"line":3099},[1153,7449,7450],{"class":1609},"      damping: 20,\n",[1153,7452,7453],{"class":1155,"line":3105},[1153,7454,7455],{"class":1609},"      delay: 0.3,\n",[1153,7457,7458],{"class":1155,"line":3111},[1153,7459,7460],{"class":1609},"    }\"\n",[1153,7462,7463],{"class":1155,"line":3117},[1153,7464,7465],{"class":1297},"  />\n",[1153,7467,7468,7470,7472],{"class":1155,"line":3282},[1153,7469,1346],{"class":1297},[1153,7471,7385],{"class":1301},[1153,7473,1305],{"class":1297},[14,7475,7476,7477],{},"📌 ",[1761,7478,7479],{},"Cocok untuk elemen yang ingin muncul dengan efek “zoom & rotate”.",[35,7481],{},[55,7483,7485,7486],{"id":7484},"_2-animasi-berurutan-timeline-style","2. ",[26,7487,7488],{},"Animasi Berurutan / Timeline-Style",[1131,7490,7492],{"className":5494,"code":7491,"language":5496,"meta":982,"style":982},"\u003Cscript setup lang=\"ts\">\nimport { Motion } from 'motion-v'\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CMotion\n    class=\"bg-primary h-20 aspect-square rounded-2xl\"\n    :animate=\"{\n      scale: [1, 2, 2, 1, 1],\n      rotate: [0, 0, 180, 180, 0],\n      borderRadius: ['0%', '0%', '50%', '50%', '0%'],\n    }\"\n    :transition=\"{\n      duration: 2,\n      ease: 'easeInOut',\n      times: [0, 0.2, 0.5, 0.8, 1],\n      repeat: Infinity,\n      repeatDelay: 1,\n    }\"\n  />\n\u003C/template>\n",[256,7493,7494,7510,7520,7528,7532,7540,7546,7555,7563,7568,7573,7578,7582,7590,7595,7600,7605,7610,7615,7619,7623],{"__ignoreMap":982},[1153,7495,7496,7498,7500,7502,7504,7506,7508],{"class":1155,"line":1156},[1153,7497,1298],{"class":1297},[1153,7499,5505],{"class":1301},[1153,7501,5508],{"class":1605},[1153,7503,5511],{"class":1605},[1153,7505,4125],{"class":1297},[1153,7507,5516],{"class":1609},[1153,7509,1305],{"class":1297},[1153,7511,7512,7514,7516,7518],{"class":1155,"line":983},[1153,7513,7357],{"class":4096},[1153,7515,7360],{"class":1297},[1153,7517,7363],{"class":4096},[1153,7519,7366],{"class":1609},[1153,7521,7522,7524,7526],{"class":1155,"line":988},[1153,7523,1346],{"class":1297},[1153,7525,5505],{"class":1301},[1153,7527,1305],{"class":1297},[1153,7529,7530],{"class":1155,"line":1172},[1153,7531,2209],{"emptyLinePlaceholder":542},[1153,7533,7534,7536,7538],{"class":1155,"line":1178},[1153,7535,1298],{"class":1297},[1153,7537,7385],{"class":1301},[1153,7539,1305],{"class":1297},[1153,7541,7542,7544],{"class":1155,"line":1184},[1153,7543,7392],{"class":1297},[1153,7545,7395],{"class":1301},[1153,7547,7548,7550,7552],{"class":1155,"line":1190},[1153,7549,7400],{"class":1605},[1153,7551,4125],{"class":1297},[1153,7553,7554],{"class":1609},"\"bg-primary h-20 aspect-square rounded-2xl\"\n",[1153,7556,7557,7559,7561],{"class":1155,"line":1196},[1153,7558,7420],{"class":1605},[1153,7560,4125],{"class":1297},[1153,7562,7435],{"class":1609},[1153,7564,7565],{"class":1155,"line":1202},[1153,7566,7567],{"class":1609},"      scale: [1, 2, 2, 1, 1],\n",[1153,7569,7570],{"class":1155,"line":1208},[1153,7571,7572],{"class":1609},"      rotate: [0, 0, 180, 180, 0],\n",[1153,7574,7575],{"class":1155,"line":3087},[1153,7576,7577],{"class":1609},"      borderRadius: ['0%', '0%', '50%', '50%', '0%'],\n",[1153,7579,7580],{"class":1155,"line":3093},[1153,7581,7460],{"class":1609},[1153,7583,7584,7586,7588],{"class":1155,"line":3099},[1153,7585,7430],{"class":1605},[1153,7587,4125],{"class":1297},[1153,7589,7435],{"class":1609},[1153,7591,7592],{"class":1155,"line":3105},[1153,7593,7594],{"class":1609},"      duration: 2,\n",[1153,7596,7597],{"class":1155,"line":3111},[1153,7598,7599],{"class":1609},"      ease: 'easeInOut',\n",[1153,7601,7602],{"class":1155,"line":3117},[1153,7603,7604],{"class":1609},"      times: [0, 0.2, 0.5, 0.8, 1],\n",[1153,7606,7607],{"class":1155,"line":3282},[1153,7608,7609],{"class":1609},"      repeat: Infinity,\n",[1153,7611,7612],{"class":1155,"line":3288},[1153,7613,7614],{"class":1609},"      repeatDelay: 1,\n",[1153,7616,7617],{"class":1155,"line":3293},[1153,7618,7460],{"class":1609},[1153,7620,7621],{"class":1155,"line":3298},[1153,7622,7465],{"class":1297},[1153,7624,7625,7627,7629],{"class":1155,"line":3303},[1153,7626,1346],{"class":1297},[1153,7628,7385],{"class":1301},[1153,7630,1305],{"class":1297},[14,7632,7476,7633],{},[1761,7634,7635],{},"Bikin efek animasi berulang seperti loading icon atau attention seeker.",[35,7637],{},[55,7639,7641,7642],{"id":7640},"_3-animasi-stagger-berurutan-dengan-variants","3. ",[26,7643,7644],{},"Animasi Stagger / Berurutan dengan Variants",[1131,7646,7648],{"className":5494,"code":7647,"language":5496,"meta":982,"style":982},"\u003Cscript setup lang=\"ts\">\nimport { Motion } from 'motion-v'\n\nconst container = {\n  hidden: { opacity: 1, scale: 0 },\n  visible: {\n    opacity: 1,\n    scale: 1,\n  },\n}\n\nconst items = {\n  hidden: { y: 20, opacity: 0, scale: 0.85 },\n  visible: {\n    y: 0,\n    opacity: 1,\n  },\n}\n\nconst list = [0, 1, 2, 3]\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CMotion\n    as=\"ul\"\n    :variants=\"container\"\n    initial=\"hidden\"\n    animate=\"visible\"\n    :transition=\"{\n      type: 'spring',\n      delayChildren: 0.5,\n      staggerChildren: 0.2,\n    }\"\n    class=\"rounded-2xl overflow-hidden  list-none p-2  grid-cols-2 grid-rows-2 aspect-square bg-primary/20 w-1/3  grid\"\n  >\n    \u003CMotion\n      v-for=\"(item, i) in list\"\n      :key=\"item\"\n      :variants=\"items\"\n      class=\"bg-primary rounded-full origin-center\"\n    />\n  \u003C/Motion>\n\u003C/template>\n",[256,7649,7650,7666,7676,7680,7691,7706,7711,7720,7729,7734,7738,7742,7753,7773,7777,7786,7794,7798,7802,7806,7834,7842,7846,7854,7860,7870,7880,7890,7900,7908,7912,7917,7922,7927,7937,7943,7951,7962,7973,7984,7995,8001,8012],{"__ignoreMap":982},[1153,7651,7652,7654,7656,7658,7660,7662,7664],{"class":1155,"line":1156},[1153,7653,1298],{"class":1297},[1153,7655,5505],{"class":1301},[1153,7657,5508],{"class":1605},[1153,7659,5511],{"class":1605},[1153,7661,4125],{"class":1297},[1153,7663,5516],{"class":1609},[1153,7665,1305],{"class":1297},[1153,7667,7668,7670,7672,7674],{"class":1155,"line":983},[1153,7669,7357],{"class":4096},[1153,7671,7360],{"class":1297},[1153,7673,7363],{"class":4096},[1153,7675,7366],{"class":1609},[1153,7677,7678],{"class":1155,"line":988},[1153,7679,2209],{"emptyLinePlaceholder":542},[1153,7681,7682,7684,7687,7689],{"class":1155,"line":1172},[1153,7683,5233],{"class":4096},[1153,7685,7686],{"class":1619}," container",[1153,7688,4591],{"class":4096},[1153,7690,4752],{"class":1297},[1153,7692,7693,7696,7698,7701,7703],{"class":1155,"line":1178},[1153,7694,7695],{"class":1297},"  hidden: { opacity: ",[1153,7697,3801],{"class":1619},[1153,7699,7700],{"class":1297},", scale: ",[1153,7702,540],{"class":1619},[1153,7704,7705],{"class":1297}," },\n",[1153,7707,7708],{"class":1155,"line":1184},[1153,7709,7710],{"class":1297},"  visible: {\n",[1153,7712,7713,7716,7718],{"class":1155,"line":1190},[1153,7714,7715],{"class":1297},"    opacity: ",[1153,7717,3801],{"class":1619},[1153,7719,4246],{"class":1297},[1153,7721,7722,7725,7727],{"class":1155,"line":1196},[1153,7723,7724],{"class":1297},"    scale: ",[1153,7726,3801],{"class":1619},[1153,7728,4246],{"class":1297},[1153,7730,7731],{"class":1155,"line":1202},[1153,7732,7733],{"class":1297},"  },\n",[1153,7735,7736],{"class":1155,"line":1208},[1153,7737,3120],{"class":1297},[1153,7739,7740],{"class":1155,"line":3087},[1153,7741,2209],{"emptyLinePlaceholder":542},[1153,7743,7744,7746,7749,7751],{"class":1155,"line":3093},[1153,7745,5233],{"class":4096},[1153,7747,7748],{"class":1619}," items",[1153,7750,4591],{"class":4096},[1153,7752,4752],{"class":1297},[1153,7754,7755,7758,7761,7764,7766,7768,7771],{"class":1155,"line":3099},[1153,7756,7757],{"class":1297},"  hidden: { y: ",[1153,7759,7760],{"class":1619},"20",[1153,7762,7763],{"class":1297},", opacity: ",[1153,7765,540],{"class":1619},[1153,7767,7700],{"class":1297},[1153,7769,7770],{"class":1619},"0.85",[1153,7772,7705],{"class":1297},[1153,7774,7775],{"class":1155,"line":3105},[1153,7776,7710],{"class":1297},[1153,7778,7779,7782,7784],{"class":1155,"line":3111},[1153,7780,7781],{"class":1297},"    y: ",[1153,7783,540],{"class":1619},[1153,7785,4246],{"class":1297},[1153,7787,7788,7790,7792],{"class":1155,"line":3117},[1153,7789,7715],{"class":1297},[1153,7791,3801],{"class":1619},[1153,7793,4246],{"class":1297},[1153,7795,7796],{"class":1155,"line":3282},[1153,7797,7733],{"class":1297},[1153,7799,7800],{"class":1155,"line":3288},[1153,7801,3120],{"class":1297},[1153,7803,7804],{"class":1155,"line":3293},[1153,7805,2209],{"emptyLinePlaceholder":542},[1153,7807,7808,7810,7812,7814,7816,7818,7820,7822,7824,7827,7829,7832],{"class":1155,"line":3298},[1153,7809,5233],{"class":4096},[1153,7811,3637],{"class":1619},[1153,7813,4591],{"class":4096},[1153,7815,4953],{"class":1297},[1153,7817,540],{"class":1619},[1153,7819,2303],{"class":1297},[1153,7821,3801],{"class":1619},[1153,7823,2303],{"class":1297},[1153,7825,7826],{"class":1619},"2",[1153,7828,2303],{"class":1297},[1153,7830,7831],{"class":1619},"3",[1153,7833,4996],{"class":1297},[1153,7835,7836,7838,7840],{"class":1155,"line":3303},[1153,7837,1346],{"class":1297},[1153,7839,5505],{"class":1301},[1153,7841,1305],{"class":1297},[1153,7843,7844],{"class":1155,"line":3308},[1153,7845,2209],{"emptyLinePlaceholder":542},[1153,7847,7848,7850,7852],{"class":1155,"line":4279},[1153,7849,1298],{"class":1297},[1153,7851,7385],{"class":1301},[1153,7853,1305],{"class":1297},[1153,7855,7856,7858],{"class":1155,"line":4288},[1153,7857,7392],{"class":1297},[1153,7859,7395],{"class":1301},[1153,7861,7862,7865,7867],{"class":1155,"line":4302},[1153,7863,7864],{"class":1605},"    as",[1153,7866,4125],{"class":1297},[1153,7868,7869],{"class":1609},"\"ul\"\n",[1153,7871,7872,7875,7877],{"class":1155,"line":4308},[1153,7873,7874],{"class":1605},"    :variants",[1153,7876,4125],{"class":1297},[1153,7878,7879],{"class":1609},"\"container\"\n",[1153,7881,7882,7885,7887],{"class":1155,"line":4314},[1153,7883,7884],{"class":1605},"    initial",[1153,7886,4125],{"class":1297},[1153,7888,7889],{"class":1609},"\"hidden\"\n",[1153,7891,7892,7895,7897],{"class":1155,"line":4319},[1153,7893,7894],{"class":1605},"    animate",[1153,7896,4125],{"class":1297},[1153,7898,7899],{"class":1609},"\"visible\"\n",[1153,7901,7902,7904,7906],{"class":1155,"line":4325},[1153,7903,7430],{"class":1605},[1153,7905,4125],{"class":1297},[1153,7907,7435],{"class":1609},[1153,7909,7910],{"class":1155,"line":4343},[1153,7911,7440],{"class":1609},[1153,7913,7914],{"class":1155,"line":4354},[1153,7915,7916],{"class":1609},"      delayChildren: 0.5,\n",[1153,7918,7919],{"class":1155,"line":4360},[1153,7920,7921],{"class":1609},"      staggerChildren: 0.2,\n",[1153,7923,7925],{"class":1155,"line":7924},33,[1153,7926,7460],{"class":1609},[1153,7928,7930,7932,7934],{"class":1155,"line":7929},34,[1153,7931,7400],{"class":1605},[1153,7933,4125],{"class":1297},[1153,7935,7936],{"class":1609},"\"rounded-2xl overflow-hidden  list-none p-2  grid-cols-2 grid-rows-2 aspect-square bg-primary/20 w-1/3  grid\"\n",[1153,7938,7940],{"class":1155,"line":7939},35,[1153,7941,7942],{"class":1297},"  >\n",[1153,7944,7946,7949],{"class":1155,"line":7945},36,[1153,7947,7948],{"class":1297},"    \u003C",[1153,7950,7395],{"class":1301},[1153,7952,7954,7957,7959],{"class":1155,"line":7953},37,[1153,7955,7956],{"class":1605},"      v-for",[1153,7958,4125],{"class":1297},[1153,7960,7961],{"class":1609},"\"(item, i) in list\"\n",[1153,7963,7965,7968,7970],{"class":1155,"line":7964},38,[1153,7966,7967],{"class":1605},"      :key",[1153,7969,4125],{"class":1297},[1153,7971,7972],{"class":1609},"\"item\"\n",[1153,7974,7976,7979,7981],{"class":1155,"line":7975},39,[1153,7977,7978],{"class":1605},"      :variants",[1153,7980,4125],{"class":1297},[1153,7982,7983],{"class":1609},"\"items\"\n",[1153,7985,7987,7990,7992],{"class":1155,"line":7986},40,[1153,7988,7989],{"class":1605},"      class",[1153,7991,4125],{"class":1297},[1153,7993,7994],{"class":1609},"\"bg-primary rounded-full origin-center\"\n",[1153,7996,7998],{"class":1155,"line":7997},41,[1153,7999,8000],{"class":1297},"    />\n",[1153,8002,8004,8007,8010],{"class":1155,"line":8003},42,[1153,8005,8006],{"class":1297},"  \u003C/",[1153,8008,8009],{"class":1301},"Motion",[1153,8011,1305],{"class":1297},[1153,8013,8015,8017,8019],{"class":1155,"line":8014},43,[1153,8016,1346],{"class":1297},[1153,8018,7385],{"class":1301},[1153,8020,1305],{"class":1297},[14,8022,7476,8023],{},[1761,8024,8025],{},"Cocok untuk grid, list, atau card yang ingin muncul satu per satu.",[35,8027],{},[55,8029,8031,8032],{"id":8030},"_4-hover-press-interaktif","4. ",[26,8033,8034],{},"Hover & Press Interaktif",[1131,8036,8038],{"className":5494,"code":8037,"language":5496,"meta":982,"style":982},"\u003Cscript setup lang=\"ts\">\nimport { Motion } from 'motion-v'\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CMotion\n    :hover=\"{ scale: 1.2, rotate: 90 }\"\n    :press=\"{\n      scale: 0.8,\n      rotate: -90,\n      borderRadius: '100%',\n    }\"\n    :transition=\"{\n      type: 'spring',\n      stiffness: 260,\n      damping: 20,\n    }\"\n    as=\"button\"\n    class=\"rounded-2xl overflow-hidden list-none p-2 grid-cols-2 grid-rows-2 aspect-square bg-primary w-1/3 grid\"\n  />\n\u003C/template>\n",[256,8039,8040,8056,8066,8074,8078,8086,8092,8102,8111,8116,8121,8126,8130,8138,8142,8146,8150,8154,8163,8172,8176],{"__ignoreMap":982},[1153,8041,8042,8044,8046,8048,8050,8052,8054],{"class":1155,"line":1156},[1153,8043,1298],{"class":1297},[1153,8045,5505],{"class":1301},[1153,8047,5508],{"class":1605},[1153,8049,5511],{"class":1605},[1153,8051,4125],{"class":1297},[1153,8053,5516],{"class":1609},[1153,8055,1305],{"class":1297},[1153,8057,8058,8060,8062,8064],{"class":1155,"line":983},[1153,8059,7357],{"class":4096},[1153,8061,7360],{"class":1297},[1153,8063,7363],{"class":4096},[1153,8065,7366],{"class":1609},[1153,8067,8068,8070,8072],{"class":1155,"line":988},[1153,8069,1346],{"class":1297},[1153,8071,5505],{"class":1301},[1153,8073,1305],{"class":1297},[1153,8075,8076],{"class":1155,"line":1172},[1153,8077,2209],{"emptyLinePlaceholder":542},[1153,8079,8080,8082,8084],{"class":1155,"line":1178},[1153,8081,1298],{"class":1297},[1153,8083,7385],{"class":1301},[1153,8085,1305],{"class":1297},[1153,8087,8088,8090],{"class":1155,"line":1184},[1153,8089,7392],{"class":1297},[1153,8091,7395],{"class":1301},[1153,8093,8094,8097,8099],{"class":1155,"line":1190},[1153,8095,8096],{"class":1605},"    :hover",[1153,8098,4125],{"class":1297},[1153,8100,8101],{"class":1609},"\"{ scale: 1.2, rotate: 90 }\"\n",[1153,8103,8104,8107,8109],{"class":1155,"line":1196},[1153,8105,8106],{"class":1605},"    :press",[1153,8108,4125],{"class":1297},[1153,8110,7435],{"class":1609},[1153,8112,8113],{"class":1155,"line":1202},[1153,8114,8115],{"class":1609},"      scale: 0.8,\n",[1153,8117,8118],{"class":1155,"line":1208},[1153,8119,8120],{"class":1609},"      rotate: -90,\n",[1153,8122,8123],{"class":1155,"line":3087},[1153,8124,8125],{"class":1609},"      borderRadius: '100%',\n",[1153,8127,8128],{"class":1155,"line":3093},[1153,8129,7460],{"class":1609},[1153,8131,8132,8134,8136],{"class":1155,"line":3099},[1153,8133,7430],{"class":1605},[1153,8135,4125],{"class":1297},[1153,8137,7435],{"class":1609},[1153,8139,8140],{"class":1155,"line":3105},[1153,8141,7440],{"class":1609},[1153,8143,8144],{"class":1155,"line":3111},[1153,8145,7445],{"class":1609},[1153,8147,8148],{"class":1155,"line":3117},[1153,8149,7450],{"class":1609},[1153,8151,8152],{"class":1155,"line":3282},[1153,8153,7460],{"class":1609},[1153,8155,8156,8158,8160],{"class":1155,"line":3288},[1153,8157,7864],{"class":1605},[1153,8159,4125],{"class":1297},[1153,8161,8162],{"class":1609},"\"button\"\n",[1153,8164,8165,8167,8169],{"class":1155,"line":3293},[1153,8166,7400],{"class":1605},[1153,8168,4125],{"class":1297},[1153,8170,8171],{"class":1609},"\"rounded-2xl overflow-hidden list-none p-2 grid-cols-2 grid-rows-2 aspect-square bg-primary w-1/3 grid\"\n",[1153,8173,8174],{"class":1155,"line":3298},[1153,8175,7465],{"class":1297},[1153,8177,8178,8180,8182],{"class":1155,"line":3303},[1153,8179,1346],{"class":1297},[1153,8181,7385],{"class":1301},[1153,8183,1305],{"class":1297},[14,8185,7476,8186],{},[1761,8187,8188],{},"Tombol jadi lebih hidup dan terasa menyenangkan!",[35,8190],{},[55,8192,8194,8195],{"id":8193},"_5-fade-in-halus-dengan-initial-animate","5. ",[26,8196,8197,8198,8200,8201],{},"Fade-in Halus dengan ",[256,8199,7296],{}," + ",[256,8202,7331],{},[1131,8204,8206],{"className":5494,"code":8205,"language":5496,"meta":982,"style":982},"\u003Cscript setup lang=\"ts\">\nimport { motion } from 'motion-v'\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cmotion.div\n    :initial=\"{ opacity: 0 }\"\n    :animate=\"{ opacity: 1 }\"\n  />\n\u003C/template>\n",[256,8207,8208,8224,8235,8243,8247,8255,8262,8271,8280,8284],{"__ignoreMap":982},[1153,8209,8210,8212,8214,8216,8218,8220,8222],{"class":1155,"line":1156},[1153,8211,1298],{"class":1297},[1153,8213,5505],{"class":1301},[1153,8215,5508],{"class":1605},[1153,8217,5511],{"class":1605},[1153,8219,4125],{"class":1297},[1153,8221,5516],{"class":1609},[1153,8223,1305],{"class":1297},[1153,8225,8226,8228,8231,8233],{"class":1155,"line":983},[1153,8227,7357],{"class":4096},[1153,8229,8230],{"class":1297}," { motion } ",[1153,8232,7363],{"class":4096},[1153,8234,7366],{"class":1609},[1153,8236,8237,8239,8241],{"class":1155,"line":988},[1153,8238,1346],{"class":1297},[1153,8240,5505],{"class":1301},[1153,8242,1305],{"class":1297},[1153,8244,8245],{"class":1155,"line":1172},[1153,8246,2209],{"emptyLinePlaceholder":542},[1153,8248,8249,8251,8253],{"class":1155,"line":1178},[1153,8250,1298],{"class":1297},[1153,8252,7385],{"class":1301},[1153,8254,1305],{"class":1297},[1153,8256,8257,8259],{"class":1155,"line":1184},[1153,8258,7392],{"class":1297},[1153,8260,8261],{"class":1301},"motion.div\n",[1153,8263,8264,8266,8268],{"class":1155,"line":1190},[1153,8265,7410],{"class":1605},[1153,8267,4125],{"class":1297},[1153,8269,8270],{"class":1609},"\"{ opacity: 0 }\"\n",[1153,8272,8273,8275,8277],{"class":1155,"line":1196},[1153,8274,7420],{"class":1605},[1153,8276,4125],{"class":1297},[1153,8278,8279],{"class":1609},"\"{ opacity: 1 }\"\n",[1153,8281,8282],{"class":1155,"line":1202},[1153,8283,7465],{"class":1297},[1153,8285,8286,8288,8290],{"class":1155,"line":1208},[1153,8287,1346],{"class":1297},[1153,8289,7385],{"class":1301},[1153,8291,1305],{"class":1297},[14,8293,7476,8294],{},[1761,8295,8296],{},"Efek fade sederhana dan ringan untuk transisi konten.",[35,8298],{},[55,8300,8302,8303],{"id":8301},"_6-animasi-saat-elemen-muncul-di-layar-scroll-in-view","6. ",[26,8304,8305],{},"Animasi Saat Elemen Muncul di Layar (Scroll In-View)",[1131,8307,8309],{"className":5494,"code":8308,"language":5496,"meta":982,"style":982},"\u003Cscript setup lang=\"ts\">\nimport { Motion } from 'motion-v'\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CMotion\n    class=\"bg-primary w-1/3 aspect-square rounded-2xl\"\n    :initial=\"{ scale: 0 }\"\n    :in-view=\"{ rotate: 180, scale: 1 }\"\n    :in-view-options=\"{\n      once: true,\n    }\"\n    :transition=\"{\n      type: 'spring',\n      stiffness: 260,\n      damping: 20,\n      delay: 0.3,\n    }\"\n  />\n\u003C/template>\n",[256,8310,8311,8327,8337,8345,8349,8357,8363,8371,8379,8388,8397,8402,8406,8414,8418,8422,8426,8430,8434,8438],{"__ignoreMap":982},[1153,8312,8313,8315,8317,8319,8321,8323,8325],{"class":1155,"line":1156},[1153,8314,1298],{"class":1297},[1153,8316,5505],{"class":1301},[1153,8318,5508],{"class":1605},[1153,8320,5511],{"class":1605},[1153,8322,4125],{"class":1297},[1153,8324,5516],{"class":1609},[1153,8326,1305],{"class":1297},[1153,8328,8329,8331,8333,8335],{"class":1155,"line":983},[1153,8330,7357],{"class":4096},[1153,8332,7360],{"class":1297},[1153,8334,7363],{"class":4096},[1153,8336,7366],{"class":1609},[1153,8338,8339,8341,8343],{"class":1155,"line":988},[1153,8340,1346],{"class":1297},[1153,8342,5505],{"class":1301},[1153,8344,1305],{"class":1297},[1153,8346,8347],{"class":1155,"line":1172},[1153,8348,2209],{"emptyLinePlaceholder":542},[1153,8350,8351,8353,8355],{"class":1155,"line":1178},[1153,8352,1298],{"class":1297},[1153,8354,7385],{"class":1301},[1153,8356,1305],{"class":1297},[1153,8358,8359,8361],{"class":1155,"line":1184},[1153,8360,7392],{"class":1297},[1153,8362,7395],{"class":1301},[1153,8364,8365,8367,8369],{"class":1155,"line":1190},[1153,8366,7400],{"class":1605},[1153,8368,4125],{"class":1297},[1153,8370,7405],{"class":1609},[1153,8372,8373,8375,8377],{"class":1155,"line":1196},[1153,8374,7410],{"class":1605},[1153,8376,4125],{"class":1297},[1153,8378,7415],{"class":1609},[1153,8380,8381,8384,8386],{"class":1155,"line":1202},[1153,8382,8383],{"class":1605},"    :in-view",[1153,8385,4125],{"class":1297},[1153,8387,7425],{"class":1609},[1153,8389,8390,8393,8395],{"class":1155,"line":1208},[1153,8391,8392],{"class":1605},"    :in-view-options",[1153,8394,4125],{"class":1297},[1153,8396,7435],{"class":1609},[1153,8398,8399],{"class":1155,"line":3087},[1153,8400,8401],{"class":1609},"      once: true,\n",[1153,8403,8404],{"class":1155,"line":3093},[1153,8405,7460],{"class":1609},[1153,8407,8408,8410,8412],{"class":1155,"line":3099},[1153,8409,7430],{"class":1605},[1153,8411,4125],{"class":1297},[1153,8413,7435],{"class":1609},[1153,8415,8416],{"class":1155,"line":3105},[1153,8417,7440],{"class":1609},[1153,8419,8420],{"class":1155,"line":3111},[1153,8421,7445],{"class":1609},[1153,8423,8424],{"class":1155,"line":3117},[1153,8425,7450],{"class":1609},[1153,8427,8428],{"class":1155,"line":3282},[1153,8429,7455],{"class":1609},[1153,8431,8432],{"class":1155,"line":3288},[1153,8433,7460],{"class":1609},[1153,8435,8436],{"class":1155,"line":3293},[1153,8437,7465],{"class":1297},[1153,8439,8440,8442,8444],{"class":1155,"line":3298},[1153,8441,1346],{"class":1297},[1153,8443,7385],{"class":1301},[1153,8445,1305],{"class":1297},[14,8447,7476,8448],{},[1761,8449,8450],{},"Efek masuk ketika user scroll — bagus untuk hero section, features, atau showcase.",[35,8452],{},[38,8454,8456],{"id":8455},"siap-coba-motion-vue","🚀 Siap Coba Motion Vue?",[14,8458,8459],{},"Kamu bisa mulai dengan:",[1131,8461,8463],{"className":1596,"code":8462,"language":1598,"meta":982,"style":982},"pnpm add motion-v\n# atau\nnpm install motion-v\n",[256,8464,8465,8475,8480],{"__ignoreMap":982},[1153,8466,8467,8470,8472],{"class":1155,"line":1156},[1153,8468,8469],{"class":1605},"pnpm",[1153,8471,1613],{"class":1609},[1153,8473,8474],{"class":1609}," motion-v\n",[1153,8476,8477],{"class":1155,"line":983},[1153,8478,8479],{"class":1310},"# atau\n",[1153,8481,8482,8484,8486],{"class":1155,"line":988},[1153,8483,2195],{"class":1605},[1153,8485,2198],{"class":1609},[1153,8487,8474],{"class":1609},[14,8489,8490],{},"Lalu gunakan seperti di contoh-contoh di atas. Simpel, ringan, dan bikin UI kamu terasa profesional dan hidup ✨",[38,8492,887],{"id":886},[14,8494,8495],{},"Motion Vue adalah pilihan tepat untuk kamu yang ingin membuat antarmuka Vue.js dengan animasi modern tanpa ribet. Dengan API yang sederhana dan performa yang optimal, kamu bisa fokus pada pengalaman pengguna tanpa harus khawatir soal kompleksitas animasi.",[38,8497,936],{"id":8498},"referensi",[63,8500,8501,8508],{},[66,8502,8503],{},[18,8504,8507],{"href":8505,"rel":8506},"https://motion.unovue.com/",[22],"Motion Vue Documentation",[66,8509,8510],{},[18,8511,7258],{"href":7256,"rel":8512},[22],[1302,8514,8515],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":982,"searchDepth":983,"depth":983,"links":8517},[8518,8524,8538,8539,8540],{"id":7249,"depth":983,"text":7250,"children":8519},[8520,8521,8522,8523],{"id":7265,"depth":988,"text":7266},{"id":7282,"depth":988,"text":7283},{"id":7289,"depth":988,"text":7290},{"id":7309,"depth":988,"text":7310},{"id":7318,"depth":983,"text":7319,"children":8525},[8526,8528,8530,8532,8534,8536],{"id":7322,"depth":988,"text":8527},"1. Animasi Dasar dengan initial dan animate",{"id":7484,"depth":988,"text":8529},"2. Animasi Berurutan / Timeline-Style",{"id":7640,"depth":988,"text":8531},"3. Animasi Stagger / Berurutan dengan Variants",{"id":8030,"depth":988,"text":8533},"4. Hover & Press Interaktif",{"id":8193,"depth":988,"text":8535},"5. Fade-in Halus dengan initial + animate",{"id":8301,"depth":988,"text":8537},"6. Animasi Saat Elemen Muncul di Layar (Scroll In-View)",{"id":8455,"depth":983,"text":8456},{"id":886,"depth":983,"text":887},{"id":8498,"depth":983,"text":936},"Motion Vue adalah library animasi untuk Vue.js yang ringan, cepat, dan mudah digunakan. Cocok untuk membuat antarmuka dinamis dengan animasi yang mulus.",{"date":8543,"keywords":8544,"tags":8545,"slug":8548},"2025-06-12","Motion Vue, Vue.js, animasi, antarmuka dinamis, web development",[7219,8546,8547,7114],"Framer Motion","Vue.js","mengenal-motion-vue-animasi-modern-dan-ringan-untuk-vuejs","/blog/mengenal-motion-vue-animasi-modern-dan-ringan-untuk-vuejs",{"title":7211,"description":8541},"blog/mengenal-motion-vue-animasi-modern-dan-ringan-untuk-vuejs","hp3-VtZCvtajgOHf44wNp9RWNBocrxfR3VvPzF3moao",{"id":8554,"title":8555,"body":8556,"description":8920,"extension":1017,"meta":8921,"navigation":542,"ogImage":8562,"path":8925,"seo":8926,"stem":8927,"__hash__":8928},"blog/blog/introducing-nuxia.md","Memperkenal Nuxia: Fullstack Starter Kit Modern Berbasis Nuxt 3",{"type":8,"value":8557,"toc":8910},[8558,8564,8568,8577,8580,8582,8586,8591,8652,8654,8658,8708,8710,8714,8728,8730,8734,8741,8743,8747,8753,8879,8886,8893,8895,8899,8902,8908],[14,8559,8560],{},[278,8561],{"src":8562,"alt":6588,"className":8563,"style":3937},"https://images.unsplash.com/photo-1717100326555-19ba2a6a1cb2?q=80&w=3474&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",[539],[38,8565,8567],{"id":8566},"nuxia-telah-resmi-diluncurkan","🚀 Nuxia Telah Resmi Diluncurkan!",[14,8569,8570,8571,1976,8573,8576],{},"Hari ini, saya dengan bangga memperkenalkan ",[26,8572,6588],{},[26,8574,8575],{},"Fullstack Starter Kit"," modern berbasis Nuxt 3 yang dibangun khusus untuk developer yang ingin membangun aplikasi web dengan cepat, aman, dan menyenangkan.",[14,8578,8579],{},"Nuxia ini terinspirasi dari Laravel Starter Kit yang dibuat oleh Tim Laravel. Bisa dibilang, Nuxia ini adalah versi Nuxt dari starter kit tersebut, dengan base Nuxt 3 yang sudah terintegrasi dengan berbagai teknologi modern.",[35,8581],{},[55,8583,8585],{"id":8584},"apa-itu-nuxia","💡 Apa itu Nuxia?",[14,8587,8588,8590],{},[26,8589,6588],{}," adalah kerangka awal (starter template) untuk membangun aplikasi fullstack modern menggunakan teknologi yang sudah terbukti dan populer:",[63,8592,8593,8599,8606,8612,8619,8629,8639,8646],{},[66,8594,7117,8595,8598],{},[26,8596,8597],{},"Nuxt"," — Framework Vue yang powerful untuk SSR, SSG, dan SPA",[66,8600,8601,8602,8605],{},"🎨 ",[26,8603,8604],{},"Tailwind CSS"," — Framework CSS utility-first untuk desain UI cepat dan konsisten",[66,8607,8608,8609,8611],{},"🧩 ",[26,8610,5647],{}," — Komponen UI berkualitas tinggi berbasis Tailwind",[66,8613,8614,8615,8618],{},"🔐 ",[26,8616,8617],{},"nuxt-auth-utils"," — Otentikasi siap pakai untuk Nuxt",[66,8620,8621,8622,8200,8625,8628],{},"🧠 ",[26,8623,8624],{},"Drizzle ORM",[26,8626,8627],{},"PostgreSQL"," — ORM TypeScript-first dan database andal",[66,8630,8631,8632,8200,8635,8638],{},"📨 ",[26,8633,8634],{},"Vue Email",[26,8636,8637],{},"Nodemailer"," — Untuk mengirim email langsung dari server",[66,8640,8641,8642,8645],{},"🔎 ",[26,8643,8644],{},"Zod"," — Validasi schema berbasis TypeScript",[66,8647,8648,8649,8651],{},"🌱 ",[26,8650,6846],{}," — Jaminan type safety di seluruh stack",[35,8653],{},[55,8655,8657],{"id":8656},"️-fitur-unggulan","⚙️ Fitur Unggulan",[63,8659,8660,8667,8674,8684,8690,8701],{},[66,8661,8662,8663,8666],{},"🚀 ",[26,8664,8665],{},"Built-in Authentication"," — dengan session-based auth dan protected routes",[66,8668,8669,8670,8673],{},"🧰 ",[26,8671,8672],{},"Form validation"," dengan Zod dan composable",[66,8675,8676,8677,8680,8681,8683],{},"📩 ",[26,8678,8679],{},"Email ready"," — integrasi ",[256,8682,8634],{}," dan Nodemailer",[66,8685,8621,8686,8689],{},[26,8687,8688],{},"ORM modern"," — langsung pakai dengan Drizzle dan migrasi otomatis",[66,8691,8692,8693,8696,8697,8700],{},"🛠️ ",[26,8694,8695],{},"Worker support"," — pakai ",[256,8698,8699],{},"nuxt-workers"," untuk task berat tanpa pusing",[66,8702,8703,8704,8707],{},"💻 ",[26,8705,8706],{},"Type-safe API"," — dari backend ke frontend",[35,8709],{},[55,8711,8713],{"id":8712},"siapa-yang-cocok-pakai-nuxia","📦 Siapa yang Cocok Pakai Nuxia?",[63,8715,8716,8719,8722,8725],{},[66,8717,8718],{},"Developer solo yang ingin mulai project dengan cepat",[66,8720,8721],{},"Startup yang butuh MVP dalam hitungan hari",[66,8723,8724],{},"Pengajar/mentor yang butuh boilerplate untuk mengajar",[66,8726,8727],{},"Siapa pun yang suka TypeScript dan Vue ❤️",[35,8729],{},[55,8731,8733],{"id":8732},"status-proyek","🧪 Status Proyek",[14,8735,8736,8737,8740],{},"Nuxia saat ini berada dalam tahap ",[26,8738,8739],{},"beta awal"," dan terus dikembangkan. Saya sangat terbuka terhadap feedback dan kontribusi dari komunitas.",[35,8742],{},[55,8744,8746],{"id":8745},"mulai-sekarang","🧭 Mulai Sekarang",[14,8748,8749,8750,1285],{},"Untuk mulai menggunakan Nuxia, kamu bisa meng-clone repositori ini dengan mudah menggunakan ",[256,8751,8752],{},"giget",[1131,8754,8756],{"className":1596,"code":8755,"language":1598,"meta":982,"style":982},"# Clone the repository using `giget`\nnpx giget@latest gh:gravitano/nuxia nuxia-app\n\ncd nuxia-app\n\n# Copy the example environment variables\ncp .env.example .env\n\n# Install dependencies\npnpm install # or yarn, bun, etc.\n\n# Create a PostgreSQL database and update the .env file\n# Make sure to set the DATABASE_URL variable in .env\n\n# Migrate the database and seed initial data\npnpm db:push # migrate the database\npnpm db:seed\n\n# Start the development server\npnpm dev\n",[256,8757,8758,8763,8776,8780,8786,8790,8795,8805,8809,8814,8823,8827,8832,8837,8841,8846,8856,8863,8867,8872],{"__ignoreMap":982},[1153,8759,8760],{"class":1155,"line":1156},[1153,8761,8762],{"class":1310},"# Clone the repository using `giget`\n",[1153,8764,8765,8767,8770,8773],{"class":1155,"line":983},[1153,8766,1606],{"class":1605},[1153,8768,8769],{"class":1609}," giget@latest",[1153,8771,8772],{"class":1609}," gh:gravitano/nuxia",[1153,8774,8775],{"class":1609}," nuxia-app\n",[1153,8777,8778],{"class":1155,"line":988},[1153,8779,2209],{"emptyLinePlaceholder":542},[1153,8781,8782,8784],{"class":1155,"line":1172},[1153,8783,1648],{"class":1619},[1153,8785,8775],{"class":1609},[1153,8787,8788],{"class":1155,"line":1178},[1153,8789,2209],{"emptyLinePlaceholder":542},[1153,8791,8792],{"class":1155,"line":1184},[1153,8793,8794],{"class":1310},"# Copy the example environment variables\n",[1153,8796,8797,8799,8802],{"class":1155,"line":1190},[1153,8798,1684],{"class":1605},[1153,8800,8801],{"class":1609}," .env.example",[1153,8803,8804],{"class":1609}," .env\n",[1153,8806,8807],{"class":1155,"line":1196},[1153,8808,2209],{"emptyLinePlaceholder":542},[1153,8810,8811],{"class":1155,"line":1202},[1153,8812,8813],{"class":1310},"# Install dependencies\n",[1153,8815,8816,8818,8820],{"class":1155,"line":1208},[1153,8817,8469],{"class":1605},[1153,8819,2198],{"class":1609},[1153,8821,8822],{"class":1310}," # or yarn, bun, etc.\n",[1153,8824,8825],{"class":1155,"line":3087},[1153,8826,2209],{"emptyLinePlaceholder":542},[1153,8828,8829],{"class":1155,"line":3093},[1153,8830,8831],{"class":1310},"# Create a PostgreSQL database and update the .env file\n",[1153,8833,8834],{"class":1155,"line":3099},[1153,8835,8836],{"class":1310},"# Make sure to set the DATABASE_URL variable in .env\n",[1153,8838,8839],{"class":1155,"line":3105},[1153,8840,2209],{"emptyLinePlaceholder":542},[1153,8842,8843],{"class":1155,"line":3111},[1153,8844,8845],{"class":1310},"# Migrate the database and seed initial data\n",[1153,8847,8848,8850,8853],{"class":1155,"line":3117},[1153,8849,8469],{"class":1605},[1153,8851,8852],{"class":1609}," db:push",[1153,8854,8855],{"class":1310}," # migrate the database\n",[1153,8857,8858,8860],{"class":1155,"line":3282},[1153,8859,8469],{"class":1605},[1153,8861,8862],{"class":1609}," db:seed\n",[1153,8864,8865],{"class":1155,"line":3288},[1153,8866,2209],{"emptyLinePlaceholder":542},[1153,8868,8869],{"class":1155,"line":3293},[1153,8870,8871],{"class":1310},"# Start the development server\n",[1153,8873,8874,8876],{"class":1155,"line":3298},[1153,8875,8469],{"class":1605},[1153,8877,8878],{"class":1609}," dev\n",[14,8880,8881,8882],{},"📖 Dokumentasi dan source code:\n👉 ",[18,8883,8884],{"href":8884,"rel":8885},"https://github.com/gravitano/nuxia",[22],[14,8887,8888,8889],{},"Demo:\n👉 ",[18,8890,8891],{"href":8891,"rel":8892},"https://nuxia.warsono.dev",[22],[35,8894],{},[55,8896,8898],{"id":8897},"️-terima-kasih","❤️ Terima Kasih",[14,8900,8901],{},"Proyek ini merupakan hasil dari eksplorasi saya dalam membangun aplikasi web modern dan sudah saya gunakan untuk side-project saya. Nuxia menggabungkan berbagai teknologi yang saya sukai dan gunakan sehari-hari sebagai frontend engineer. Saya berharap Nuxia bisa menjadi alat yang berguna bagi banyak developer untuk membangun aplikasi hebat dengan lebih mudah.",[14,8903,8904,8907],{},[26,8905,8906],{},"Yuk coba Nuxia dan bantu saya menyebarkan ke teman-temanmu!"," 🚀",[1302,8909,2601],{},{"title":982,"searchDepth":983,"depth":983,"links":8911},[8912],{"id":8566,"depth":983,"text":8567,"children":8913},[8914,8915,8916,8917,8918,8919],{"id":8584,"depth":988,"text":8585},{"id":8656,"depth":988,"text":8657},{"id":8712,"depth":988,"text":8713},{"id":8732,"depth":988,"text":8733},{"id":8745,"depth":988,"text":8746},{"id":8897,"depth":988,"text":8898},"Nuxia adalah starter kit modern untuk membangun aplikasi fullstack dengan Nuxt 3, Tailwind CSS, dan teknologi terkini lainnya.",{"date":8922,"keywords":8923,"tags":8924},"2025-06-08","Nuxia, Nuxt 3, Tailwind CSS, shadcn-vue, nuxt-auth-utils, Drizzle ORM, PostgreSQL, Vue Email, Nodemailer, Zod, TypeScript",[6588,826,8604,5647,8617,8624,8627,8634,8637],"/blog/introducing-nuxia",{"title":8555,"description":8920},"blog/introducing-nuxia","tWEErS9sS4yqWlkmimg0ZlEb0lIiNvqwoauEfrdlKI8",{"id":8930,"title":8931,"body":8932,"description":9304,"extension":1017,"meta":9305,"navigation":542,"ogImage":8938,"path":9312,"seo":9313,"stem":9314,"__hash__":9315},"blog/blog/mengenal-inertia-js-cara-modern-membangun-aplikasi-monolith-yang-interaktif.md","Mengenal Inertia.js: Cara Modern Membangun Aplikasi Monolith yang Interaktif",{"type":8,"value":8933,"toc":9292},[8934,8941,8944,8950,8954,8965,8968,8972,8979,8986,8988,8992,8995,9021,9023,9027,9030,9047,9051,9096,9170,9239,9241,9245,9248,9259,9261,9265,9268,9279,9281,9283,9286,9289],[14,8935,8936],{},[278,8937],{"src":8938,"alt":865,"className":8939,"style":8940},"https://images.unsplash.com/photo-1653200256706-a5bc2f69abb7?q=80&w=3458&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",[539],"view-transition-name: blog-post-image-mengenal-inertia-js-cara-modern-membangun-aplikasi-monolith-yang-interaktif;",[1959,8942,8931],{"id":8943},"mengenal-inertiajs-cara-modern-membangun-aplikasi-monolith-yang-interaktif",[14,8945,8946,8947,8949],{},"Dalam beberapa tahun terakhir, pendekatan pengembangan web terus berkembang. Framework seperti Laravel, Rails, dan Django yang awalnya hanya fokus pada server-side rendering kini mulai \"berbaur\" dengan JavaScript framework seperti Vue, React, atau Svelte. Namun, menggabungkan keduanya seringkali kompleks — di sinilah ",[26,8948,865],{}," hadir sebagai solusi yang elegan.",[38,8951,8953],{"id":8952},"apa-itu-inertiajs","Apa Itu Inertia.js?",[14,8955,8956,3316,8958,8961,8962,33],{},[26,8957,865],{},[1761,8959,8960],{},"modern monolith tool"," yang memungkinkan kita membangun aplikasi single-page (SPA) menggunakan framework backend tradisional (seperti Laravel) dan frontend modern (seperti Vue, React, atau Svelte) ",[26,8963,8964],{},"tanpa harus membangun API",[14,8966,8967],{},"Inertia menjembatani backend dan frontend dengan cara yang sangat sederhana: ia mengatur pertukaran data antara backend dan frontend melalui response JSON, dan mengatur navigasi SPA secara otomatis.",[55,8969,8971],{"id":8970},"filosofi-inertiajs","Filosofi Inertia.js",[11,8973,8974],{},[14,8975,8976],{},[1761,8977,8978],{},"\"Like server-side routing, but with client-side rendering.\"",[14,8980,8981,8982,8985],{},"Inertia tidak mencoba menggantikan backend framework atau frontend framework. Ia hanya menjadi ",[1761,8983,8984],{},"glue"," — lem perekat yang memungkinkan keduanya bekerja harmonis. Dengan Inertia, kita bisa menulis controller di Laravel seperti biasa, lalu mengembalikan view ke komponen Vue/React, bukan ke Blade template.",[35,8987],{},[38,8989,8991],{"id":8990},"mengapa-menggunakan-inertiajs","Mengapa Menggunakan Inertia.js?",[14,8993,8994],{},"Berikut beberapa alasan kuat mengapa banyak developer menyukai Inertia.js:",[145,8996,8997,9003,9009,9015],{},[66,8998,8999,9002],{},[26,9000,9001],{},"Tidak Perlu API","\nTidak ada kebutuhan untuk membuat REST atau GraphQL API. Kamu bisa langsung melempar data dari controller ke komponen frontend.",[66,9004,9005,9008],{},[26,9006,9007],{},"Routing Server-Side","\nRouting tetap dikelola oleh backend, seperti di Laravel, sehingga struktur tetap familiar.",[66,9010,9011,9014],{},[26,9012,9013],{},"SEO-Friendly","\nDengan konfigurasi yang tepat (misalnya menggunakan SSR di Inertia + Vue), kamu bisa tetap menjaga performa SEO.",[66,9016,9017,9020],{},[26,9018,9019],{},"Progressive Enhancement","\nKamu bisa memigrasi proyek Laravel/Blade ke Vue/React secara bertahap tanpa harus menulis ulang semuanya.",[35,9022],{},[38,9024,9026],{"id":9025},"cara-kerja-inertiajs","Cara Kerja Inertia.js",[14,9028,9029],{},"Cara kerja Inertia bisa diibaratkan seperti berikut:",[145,9031,9032,9035,9038,9044],{},[66,9033,9034],{},"User melakukan request ke URL tertentu.",[66,9036,9037],{},"Server (misalnya Laravel) menangani request dan mengembalikan \"Inertia response\" berupa komponen frontend + data.",[66,9039,9040,9041,33],{},"Inertia di sisi frontend mengganti halaman secara ",[1761,9042,9043],{},"client-side",[66,9045,9046],{},"Navigasi selanjutnya dilakukan secara SPA tanpa reload, tapi tetap menggunakan route Laravel.",[55,9048,9050],{"id":9049},"contoh-sederhana-laravel-vue-inertia","Contoh Sederhana: Laravel + Vue + Inertia",[1131,9052,9056],{"className":9053,"code":9054,"language":9055,"meta":982,"style":982},"language-php shiki shiki-themes github-light github-dark","// routes/web.php\nRoute::get('/dashboard', [DashboardController::class, 'index']);\n","php",[256,9057,9058,9063],{"__ignoreMap":982},[1153,9059,9060],{"class":1155,"line":1156},[1153,9061,9062],{"class":1310},"// routes/web.php\n",[1153,9064,9065,9068,9071,9074,9076,9079,9082,9085,9088,9090,9093],{"class":1155,"line":983},[1153,9066,9067],{"class":1619},"Route",[1153,9069,9070],{"class":4096},"::",[1153,9072,9073],{"class":1605},"get",[1153,9075,4404],{"class":1297},[1153,9077,9078],{"class":1609},"'/dashboard'",[1153,9080,9081],{"class":1297},", [",[1153,9083,9084],{"class":1619},"DashboardController",[1153,9086,9087],{"class":4096},"::class",[1153,9089,2303],{"class":1297},[1153,9091,9092],{"class":1609},"'index'",[1153,9094,9095],{"class":1297},"]);\n",[1131,9097,9099],{"className":9053,"code":9098,"language":9055,"meta":982,"style":982},"// app/Http/Controllers/DashboardController.php\npublic function index()\n{\n   return Inertia::render('Dashboard', [\n      'user' => Auth::user(),\n   ]);\n}\n",[256,9100,9101,9106,9118,9122,9143,9161,9166],{"__ignoreMap":982},[1153,9102,9103],{"class":1155,"line":1156},[1153,9104,9105],{"class":1310},"// app/Http/Controllers/DashboardController.php\n",[1153,9107,9108,9110,9112,9115],{"class":1155,"line":983},[1153,9109,5347],{"class":4096},[1153,9111,5273],{"class":4096},[1153,9113,9114],{"class":1605}," index",[1153,9116,9117],{"class":1297},"()\n",[1153,9119,9120],{"class":1155,"line":988},[1153,9121,3039],{"class":1297},[1153,9123,9124,9127,9130,9132,9135,9137,9140],{"class":1155,"line":1172},[1153,9125,9126],{"class":4096},"   return",[1153,9128,9129],{"class":1619}," Inertia",[1153,9131,9070],{"class":4096},[1153,9133,9134],{"class":1605},"render",[1153,9136,4404],{"class":1297},[1153,9138,9139],{"class":1609},"'Dashboard'",[1153,9141,9142],{"class":1297},", [\n",[1153,9144,9145,9148,9150,9153,9155,9158],{"class":1155,"line":1178},[1153,9146,9147],{"class":1609},"      'user'",[1153,9149,5562],{"class":4096},[1153,9151,9152],{"class":1619}," Auth",[1153,9154,9070],{"class":4096},[1153,9156,9157],{"class":1605},"user",[1153,9159,9160],{"class":1297},"(),\n",[1153,9162,9163],{"class":1155,"line":1184},[1153,9164,9165],{"class":1297},"   ]);\n",[1153,9167,9168],{"class":1155,"line":1190},[1153,9169,3120],{"class":1297},[1131,9171,9173],{"className":5494,"code":9172,"language":5496,"meta":982,"style":982},"\u003C!-- resources/js/Pages/Dashboard.vue -->\n\u003Cscript setup>\ndefineProps({ user: Object })\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Ch1>Halo, {{ user.name }}!\u003C/h1>\n\u003C/template>\n",[256,9174,9175,9180,9190,9198,9206,9210,9218,9231],{"__ignoreMap":982},[1153,9176,9177],{"class":1155,"line":1156},[1153,9178,9179],{"class":1310},"\u003C!-- resources/js/Pages/Dashboard.vue -->\n",[1153,9181,9182,9184,9186,9188],{"class":1155,"line":983},[1153,9183,1298],{"class":1297},[1153,9185,5505],{"class":1301},[1153,9187,5508],{"class":1605},[1153,9189,1305],{"class":1297},[1153,9191,9192,9195],{"class":1155,"line":988},[1153,9193,9194],{"class":1605},"defineProps",[1153,9196,9197],{"class":1297},"({ user: Object })\n",[1153,9199,9200,9202,9204],{"class":1155,"line":1172},[1153,9201,1346],{"class":1297},[1153,9203,5505],{"class":1301},[1153,9205,1305],{"class":1297},[1153,9207,9208],{"class":1155,"line":1178},[1153,9209,2209],{"emptyLinePlaceholder":542},[1153,9211,9212,9214,9216],{"class":1155,"line":1184},[1153,9213,1298],{"class":1297},[1153,9215,7385],{"class":1301},[1153,9217,1305],{"class":1297},[1153,9219,9220,9222,9224,9227,9229],{"class":1155,"line":1190},[1153,9221,7392],{"class":1297},[1153,9223,1959],{"class":1301},[1153,9225,9226],{"class":1297},">Halo, {{ user.name }}!\u003C/",[1153,9228,1959],{"class":1301},[1153,9230,1305],{"class":1297},[1153,9232,9233,9235,9237],{"class":1155,"line":1196},[1153,9234,1346],{"class":1297},[1153,9236,7385],{"class":1301},[1153,9238,1305],{"class":1297},[35,9240],{},[38,9242,9244],{"id":9243},"kekurangan-inertiajs","Kekurangan Inertia.js",[14,9246,9247],{},"Tentu tidak ada tool yang sempurna. Beberapa kekurangan Inertia antara lain:",[63,9249,9250,9253,9256],{},[66,9251,9252],{},"Dokumentasi bisa terasa kurang mendalam untuk kasus-kasus kompleks.",[66,9254,9255],{},"Tidak cocok jika kamu memang butuh arsitektur microservices atau API-first.",[66,9257,9258],{},"Memerlukan pemahaman backend dan frontend yang cukup seimbang.",[35,9260],{},[38,9262,9264],{"id":9263},"kapan-harus-menggunakan-inertiajs","Kapan Harus Menggunakan Inertia.js?",[14,9266,9267],{},"Gunakan Inertia.js jika:",[63,9269,9270,9273,9276],{},[66,9271,9272],{},"✅ Kamu ingin membangun SPA tapi tetap menggunakan framework backend favoritmu.",[66,9274,9275],{},"✅ Kamu ingin menghindari kompleksitas pembuatan API.",[66,9277,9278],{},"✅ Kamu butuh pengalaman frontend modern tanpa meninggalkan stack Laravel/Django/Rails.",[35,9280],{},[38,9282,2549],{"id":2548},[14,9284,9285],{},"Inertia.js adalah solusi cerdas untuk developer yang ingin menggabungkan kekuatan backend dan kenyamanan frontend modern tanpa ribet. Ia bukan framework frontend, bukan juga API generator — tapi jembatan sederhana yang sangat memudahkan.",[14,9287,9288],{},"Jika kamu ingin membangun aplikasi yang modern, cepat, dan tetap efisien, Inertia.js layak dicoba.",[1302,9290,9291],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":982,"searchDepth":983,"depth":983,"links":9293},[9294,9297,9298,9301,9302,9303],{"id":8952,"depth":983,"text":8953,"children":9295},[9296],{"id":8970,"depth":988,"text":8971},{"id":8990,"depth":983,"text":8991},{"id":9025,"depth":983,"text":9026,"children":9299},[9300],{"id":9049,"depth":988,"text":9050},{"id":9243,"depth":983,"text":9244},{"id":9263,"depth":983,"text":9264},{"id":2548,"depth":983,"text":2549},"Inertia.js adalah solusi untuk membangun aplikasi monolith modern dengan Vue, React, atau Svelte tanpa perlu API.",{"date":9306,"keywords":9307,"tags":9308},"2025-06-05","Inertia.js, Vue.js, React, Svelte, aplikasi monolith, web development",[865,8547,7114,9309,9310,9311],"Svelte","Laravel","Monolith","/blog/mengenal-inertia-js-cara-modern-membangun-aplikasi-monolith-yang-interaktif",{"title":8931,"description":9304},"blog/mengenal-inertia-js-cara-modern-membangun-aplikasi-monolith-yang-interaktif","rvLeeLeRgzlIW9ezLGUOoteFfAC7UoE_Fzw__lyvaz4",{"id":9317,"title":9318,"body":9319,"description":9479,"extension":1017,"meta":9480,"navigation":542,"ogImage":9325,"path":9487,"seo":9488,"stem":9489,"__hash__":9490},"blog/blog/perjalanan-karir-saya-sebagai-frontend-developer.md","Perjalanan Karir Saya sebagai Frontend Developer",{"type":8,"value":9320,"toc":9470},[9321,9328,9331,9334,9341,9344,9348,9351,9360,9366,9370,9380,9383,9387,9390,9426,9429,9433,9436,9462,9464,9467],[14,9322,9323],{},[278,9324],{"src":9325,"alt":8547,"className":9326,"style":9327},"https://images.unsplash.com/photo-1607799279861-4dd421887fb3?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",[539],"view-transition-name: blog-post-image-perjalanan-karir-saya-sebagai-frontend-developer;",[38,9329,9318],{"id":9330},"perjalanan-karir-saya-sebagai-frontend-developer",[14,9332,9333],{},"Setiap orang punya jalur karir yang unik — dan inilah cerita saya.",[14,9335,9336,9337,9340],{},"Saya memulai perjalanan di dunia software engineering bukan langsung sebagai frontend developer. Justru, saya mengawali dari ",[26,9338,9339],{},"backend developer",". Saat itu saya lebih sering berkutat dengan API, database, dan business logic di balik layar. Tapi seiring waktu, rasa penasaran saya terhadap dunia UI/UX dan interaksi pengguna makin besar.",[14,9342,9343],{},"Akhirnya, saya memberanikan diri untuk pindah haluan: fokus di frontend. Yang mana kebetulan waktu itu, teknologi frontend sedang berkembang pesat. Framework seperti Vue.js dan React mulai populer, dan saya merasa ini adalah waktu yang tepat untuk terjun ke dunia yang lebih visual dan interaktif. Plus, di tempat saya bekerja, ada banyak proyek yang membutuhkan keahlian frontend.",[55,9345,9347],{"id":9346},"dari-backend-ke-frontend","Dari Backend ke Frontend",[14,9349,9350],{},"Transisi ini tidak selalu mulus. Di backend, saya terbiasa dengan struktur dan alur data yang relatif stabil. Tapi di frontend, saya harus mulai berpikir lebih visual dan interaktif. Mulai dari memahami DOM, CSS, reactivity, hingga rendering performance — semua terasa seperti dunia baru.",[14,9352,9353,9354,9356,9357,9359],{},"Framework pertama yang benar-benar saya pelajari secara mendalam adalah ",[26,9355,8547],{},". Saat itu Vue terasa sangat intuitif dan mudah dipelajari. Saya suka bagaimana struktur komponennya jelas, dan bagaimana pendekatan data-reactive-nya mempermudah banyak hal dan ",[256,9358,7143],{}," membuat binding data jadi sangat mudah.",[14,9361,9362,9363,9365],{},"Setelah cukup nyaman di Vue, saya juga mulai mendalami ",[26,9364,7114],{}," untuk memperluas perspektif. Sekarang saya merasa dua-duanya punya kelebihan masing-masing, dan keduanya sering saya gunakan di proyek berbeda.",[55,9367,9369],{"id":9368},"kenapa-saya-bertahan-di-frontend","Kenapa Saya Bertahan di Frontend?",[14,9371,9372,9373,9375,9376,9379],{},"Frontend itu dinamis. Dunia frontend cepat berubah, dan kadang memang bikin pusing. Tapi justru itu yang bikin saya betah: selalu ada hal baru yang bisa dipelajari, dicoba, dan dioptimalkan. Mulai dari framework baru, teknik styling seperti ",[26,9374,8604],{},", atau pattern arsitektur seperti ",[26,9377,9378],{},"Atomic Design"," dan lainnya.",[14,9381,9382],{},"Yang paling memuaskan dari kerja di frontend adalah ketika kita bisa melihat langsung hasil kerja kita — dan tahu bahwa hal kecil seperti \"loading state yang halus\" atau \"form yang ramah pengguna\" bisa membuat pengalaman pengguna jauh lebih baik.",[55,9384,9386],{"id":9385},"teknologi-yang-saya-gunakan-sehari-hari","Teknologi yang Saya Gunakan Sehari-hari",[14,9388,9389],{},"Seiring waktu, saya mulai mengembangkan style dan tools favorit sendiri. Saat ini, stack yang paling sering saya pakai meliputi:",[63,9391,9392,9398,9404,9409,9414,9420],{},[66,9393,9394,9397],{},[26,9395,9396],{},"Vue 3 / Nuxt 3"," untuk proyek yang butuh struktur yang rapi dan SSR-ready.",[66,9399,9400,9403],{},[26,9401,9402],{},"React / Next.js"," untuk proyek yang mengandalkan ekosistem besar dan fleksibilitas tinggi.",[66,9405,9406,9408],{},[26,9407,8604],{}," untuk styling cepat, konsisten, dan scalable.",[66,9410,9411,9413],{},[26,9412,6846],{}," untuk menjaga kualitas code dan mengurangi bug saat scaling.",[66,9415,9416,9419],{},[26,9417,9418],{},"shadcn/ui"," untuk komponen UI yang mudah digunakan, gampang di-custom, dan tetap menjaga konsistensi desain.",[66,9421,9422,9425],{},[26,9423,9424],{},"motion"," untuk membuat animasi yang halus dan menarik.",[14,9427,9428],{},"Saya juga cukup rajin mengotak-atik tooling: dari bundler, CI/CD, hingga performance profiling.",[55,9430,9432],{"id":9431},"apa-yang-saya-pelajari-dari-perjalanan-ini","Apa yang Saya Pelajari dari Perjalanan Ini",[14,9434,9435],{},"Beberapa hal penting yang saya pelajari sejauh ini:",[63,9437,9438,9444,9450,9456],{},[66,9439,9440,9443],{},[26,9441,9442],{},"Coding itu penting, tapi komunikasi juga."," Terutama saat bekerja dalam tim.",[66,9445,9446,9449],{},[26,9447,9448],{},"Jangan fanatik sama tools."," Setiap proyek punya kebutuhan sendiri. Pilih tools yang sesuai.",[66,9451,9452,9455],{},[26,9453,9454],{},"Terus belajar dan eksplorasi."," Dunia frontend terlalu cepat berubah untuk merasa nyaman di zona aman.",[66,9457,9458,9461],{},[26,9459,9460],{},"Dokumentasikan prosesmu."," Bahkan kesalahan bisa jadi pelajaran berharga kalau dicatat.",[55,9463,2549],{"id":2548},[14,9465,9466],{},"Saya nggak bilang perjalanan ini mudah. Tapi saya bisa bilang dengan yakin: saya menikmati setiap prosesnya. Dan lewat blog ini, saya ingin membagikan apa yang saya pelajari — siapa tahu bisa membantu, menginspirasi, atau sekadar menemani kamu yang sedang ada di jalur yang sama.",[14,9468,9469],{},"Kalau kamu punya cerita serupa atau ingin ngobrol seputar frontend, feel free buat reach out! Sampai jumpa di tulisan selanjutnya ✌️.",{"title":982,"searchDepth":983,"depth":983,"links":9471},[9472],{"id":9330,"depth":983,"text":9318,"children":9473},[9474,9475,9476,9477,9478],{"id":9346,"depth":988,"text":9347},{"id":9368,"depth":988,"text":9369},{"id":9385,"depth":988,"text":9386},{"id":9431,"depth":988,"text":9432},{"id":2548,"depth":988,"text":2549},"Cerita perjalanan saya dari seorang backend developer hingga menjadi frontend developer yang fokus pada Vue, Nuxt, Tailwind, TypeScript dan kawan-kawannya.",{"date":9481,"keywords":9482,"tags":9483},"2025-06-03","frontend developer, perjalanan karir, Vue.js, React, Tailwind CSS, TypeScript, developer Indonesia, pengalaman kerja, web development",[9484,6076,8547,7114,8604,6846,9485,9486],"frontend","pengalaman kerja","web development","/blog/perjalanan-karir-saya-sebagai-frontend-developer",{"title":9318,"description":9479},"blog/perjalanan-karir-saya-sebagai-frontend-developer","j2a7-tnPF7cf-BNEUBlUs7HFBOv58S7c9AnSGzIKjrU",1776503580413]