@@ -702,7 +702,8 @@ <h2>Integrations & Security</h2>
702702 < span id ="whatsapp-btn-icon "> 📱</ span > < span id ="whatsapp-btn-label "> Connect WhatsApp</ span >
703703 </ button >
704704 < div id ="whatsapp-qr-area " style ="display:none;margin-top:0.75rem ">
705- < pre id ="whatsapp-qr-output " style ="background:var(--card);border:1px solid var(--border);padding:1rem;border-radius:8px;font-size:0.65rem;line-height:1.15;overflow:auto;max-height:360px;white-space:pre;color:var(--text) "> Starting…</ pre >
705+ < canvas id ="whatsapp-qr-canvas " style ="display:none;border-radius:8px;max-width:100%;background:#fff;padding:8px "> </ canvas >
706+ < pre id ="whatsapp-qr-log " style ="background:var(--card);border:1px solid var(--border);padding:0.75rem;border-radius:8px;font-size:0.75rem;line-height:1.4;overflow:auto;max-height:180px;white-space:pre-wrap;color:var(--text-muted);margin-top:0.5rem "> Waiting…</ pre >
706707 </ div >
707708 < div style ="margin-top:0.75rem ">
708709 < label style ="display:flex;align-items:center;gap:10px;cursor:pointer;font-weight:400 ">
@@ -932,32 +933,94 @@ <h2 style="text-align:center">Configuration Saved!</h2>
932933 } catch { }
933934 }
934935
936+ // Decode Unicode block characters into QR pixel data and draw on canvas
937+ function renderBlockQr ( lines , canvas ) {
938+ const moduleSize = 6 ;
939+ const cols = Math . max ( ...lines . map ( l => [ ...l ] . length ) ) ;
940+ const rows = lines . length * 2 ;
941+ canvas . width = cols * moduleSize ;
942+ canvas . height = rows * moduleSize ;
943+ const ctx = canvas . getContext ( "2d" ) ;
944+ ctx . fillStyle = "#ffffff" ;
945+ ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
946+ ctx . fillStyle = "#000000" ;
947+ for ( let li = 0 ; li < lines . length ; li ++ ) {
948+ const chars = [ ...lines [ li ] ] ;
949+ for ( let ci = 0 ; ci < chars . length ; ci ++ ) {
950+ let t = false , b = false ;
951+ switch ( chars [ ci ] ) {
952+ case "\u2588" : t = true ; b = true ; break ; // █
953+ case "\u2580" : t = true ; break ; // ▀
954+ case "\u2584" : b = true ; break ; // ▄
955+ }
956+ if ( t ) ctx . fillRect ( ci * moduleSize , li * 2 * moduleSize , moduleSize , moduleSize ) ;
957+ if ( b ) ctx . fillRect ( ci * moduleSize , ( li * 2 + 1 ) * moduleSize , moduleSize , moduleSize ) ;
958+ }
959+ }
960+ canvas . style . display = "block" ;
961+ }
962+
963+ function tryRenderAsciiQr ( text , canvas ) {
964+ const allLines = text . split ( "\n" ) ;
965+ let best = [ ] , cur = [ ] ;
966+ for ( const line of allLines ) {
967+ const t = line . trimEnd ( ) ;
968+ if ( / [ \u2588 \u2580 \u2584 ] / . test ( t ) && [ ...t ] . length >= 15 ) {
969+ cur . push ( t ) ;
970+ } else {
971+ if ( cur . length > best . length ) best = cur ;
972+ cur = [ ] ;
973+ }
974+ }
975+ if ( cur . length > best . length ) best = cur ;
976+ if ( best . length < 8 ) return false ;
977+ renderBlockQr ( best , canvas ) ;
978+ return true ;
979+ }
980+
935981 function startWhatsAppLogin ( ) {
936982 const qrArea = document . getElementById ( "whatsapp-qr-area" ) ;
937- const qrOutput = document . getElementById ( "whatsapp-qr-output" ) ;
983+ const qrCanvas = document . getElementById ( "whatsapp-qr-canvas" ) ;
984+ const qrLog = document . getElementById ( "whatsapp-qr-log" ) ;
938985 const btn = document . getElementById ( "whatsapp-connect-btn" ) ;
939986
940987 qrArea . style . display = "block" ;
941- qrOutput . textContent = "Starting WhatsApp login…" ;
988+ qrCanvas . style . display = "none" ;
989+ qrLog . textContent = "Connecting\u2026" ;
942990 btn . disabled = true ;
943991
944992 if ( whatsappEventSource ) { whatsappEventSource . close ( ) ; whatsappEventSource = null ; }
945993
946994 whatsappEventSource = new EventSource ( "/api/whatsapp/login-stream" ) ;
947- let buf = "" ;
995+ let logBuf = "" ;
996+ let qrRendered = false ;
948997
949- whatsappEventSource . onmessage = ( e ) => {
950- const payload = JSON . parse ( e . data ) ;
951- if ( payload && typeof payload === "object" && payload . done ) {
998+ whatsappEventSource . onmessage = async ( e ) => {
999+ const msg = JSON . parse ( e . data ) ;
1000+ if ( msg . type === "qr" ) {
1001+ // Raw QR string — render with QRCode.js
1002+ try {
1003+ qrCanvas . style . display = "block" ;
1004+ await QRCode . toCanvas ( qrCanvas , msg . qr , { width : 256 , margin : 2 , color : { dark : "#000" , light : "#fff" } } ) ;
1005+ qrLog . textContent = "Scan with WhatsApp \u2192 Linked Devices \u2192 Link a Device" ;
1006+ qrRendered = true ;
1007+ } catch { qrLog . textContent += "\n[raw qr: " + msg . qr + "]" ; }
1008+ } else if ( msg . type === "log" ) {
1009+ logBuf += msg . data ;
1010+ // Show only non-block-char lines in the status log
1011+ const statusLines = logBuf . split ( "\n" ) . filter ( l => ! / [ \u2588 \u2580 \u2584 ] / . test ( l ) && l . trim ( ) ) . join ( "\n" ) ;
1012+ if ( statusLines ) qrLog . textContent = statusLines ;
1013+ // Try to extract and render ASCII art QR to canvas
1014+ if ( ! qrRendered ) {
1015+ qrRendered = tryRenderAsciiQr ( logBuf , qrCanvas ) ;
1016+ if ( qrRendered ) qrLog . textContent = "Scan with WhatsApp \u2192 Linked Devices \u2192 Link a Device" ;
1017+ }
1018+ } else if ( msg . type === "done" ) {
9521019 whatsappEventSource . close ( ) ;
9531020 whatsappEventSource = null ;
9541021 btn . disabled = false ;
9551022 checkWhatsAppLinked ( ) ;
956- return ;
9571023 }
958- buf += payload ;
959- qrOutput . textContent = buf ;
960- qrOutput . scrollTop = qrOutput . scrollHeight ;
9611024 } ;
9621025
9631026 whatsappEventSource . onerror = ( ) => {
@@ -2034,6 +2097,7 @@ <h2>Configure ${p.name}</h2>
20342097 }
20352098 }
20362099 </ script >
2100+ < script src ="https://cdn.jsdelivr.net/npm/qrcode@1.5.4/build/qrcode.min.js "> </ script >
20372101</ body >
20382102
20392103</ html >
0 commit comments